1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-09-27 08:54:48 +00:00

ecs: block statement expr

This commit is contained in:
Steven Hugg 2022-02-25 11:45:13 -06:00
parent 05965397fd
commit fc79cf9b0f
2 changed files with 75 additions and 34 deletions

View File

@ -2,7 +2,7 @@
import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer"; import { mergeLocs, Token, Tokenizer, TokenType } from "../tokenizer";
import { SourceLocated, SourceLocation } from "../workertypes"; import { SourceLocated, SourceLocation } from "../workertypes";
import { newDecoder } from "./decoder"; import { newDecoder } from "./decoder";
import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair, Expr, ExprBase, ForwardRef, isLiteral, EntitySetField, LExpr } from "./ecs"; import { Action, ActionContext, ActionNode, ActionWithJoin, ArrayType, CodeLiteralNode, CodePlaceholderNode, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SELECT_TYPE, SourceFileExport, System, SystemInstance, SystemInstanceParameters, ComponentFieldPair, Expr, ExprBase, ForwardRef, isLiteral, EntityFieldOp, LExpr, Statement } from "./ecs";
export enum ECSTokenType { export enum ECSTokenType {
Ellipsis = 'ellipsis', Ellipsis = 'ellipsis',
@ -349,10 +349,9 @@ export class ECSCompiler extends Tokenizer {
tempbytes = this.parseIntegerConstant(); tempbytes = this.parseIntegerConstant();
} }
let system: System = { name, tempbytes, actions: [] }; let system: System = { name, tempbytes, actions: [] };
let context: ActionContext = { scope: null, system };
let text = this.parseCode(context);
let select: SelectType = 'once'; let select: SelectType = 'once';
let action: Action = { text, event: name, select }; let expr = this.parseBlockStatement();
let action: Action = { expr, event: name, select };
system.actions.push(action); system.actions.push(action);
return system; return system;
} }
@ -384,13 +383,11 @@ export class ECSCompiler extends Tokenizer {
if (this.ifToken('fit')) { if (this.ifToken('fit')) {
fitbytes = this.parseIntegerConstant(); fitbytes = this.parseIntegerConstant();
} }
let context: ActionContext = { scope: null, system };
// parse --- code ---
let text = this.parseCode(context);
let direction = undefined; let direction = undefined;
if (modifiers['asc']) direction = 'asc'; if (modifiers['asc']) direction = 'asc';
else if (modifiers['desc']) direction = 'desc'; else if (modifiers['desc']) direction = 'desc';
let action = { text, event, query, join, select, direction, fitbytes }; let expr = this.parseBlockStatement();
let action = { expr, event, query, join, select, direction, fitbytes };
if (modifiers['critical']) (action as ActionWithJoin).critical = true; if (modifiers['critical']) (action as ActionWithJoin).critical = true;
return action as ActionWithJoin; return action as ActionWithJoin;
} }
@ -441,7 +438,7 @@ export class ECSCompiler extends Tokenizer {
return this.parseList(this.parseEventName, ","); return this.parseList(this.parseEventName, ",");
} }
parseCode(context: ActionContext): string { // TODOActionNode[] { parseCode(): string { // TODOActionNode[] {
// TODO: add $loc // TODO: add $loc
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]); let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
let code = tok.str.substring(3, tok.str.length - 3); let code = tok.str.substring(3, tok.str.length - 3);
@ -450,8 +447,8 @@ export class ECSCompiler extends Tokenizer {
if (this.includeDebugInfo) this.addDebugInfo(lines, tok.$loc.line); if (this.includeDebugInfo) this.addDebugInfo(lines, tok.$loc.line);
code = lines.join('\n'); code = lines.join('\n');
let acomp = new ECSActionCompiler(context); //let acomp = new ECSActionCompiler(context);
let nodes = acomp.parseFile(code, this.path); //let nodes = acomp.parseFile(code, this.path);
// TODO: return nodes // TODO: return nodes
return code; return code;
} }
@ -694,8 +691,8 @@ export class ECSCompiler extends Tokenizer {
} }
var opfn = getOperator(op.str).f; var opfn = getOperator(op.str).f;
// use logical operators instead of bitwise? // use logical operators instead of bitwise?
if (op.str == 'AND') opfn = 'land'; if (op.str == 'and') opfn = 'land';
if (op.str == 'OR') opfn = 'lor'; if (op.str == 'or') opfn = 'lor';
var valtype = this.exprTypeForOp(opfn, left, right, op); var valtype = this.exprTypeForOp(opfn, left, right, op);
left = { valtype:valtype, op:opfn, left: left, right: right }; left = { valtype:valtype, op:opfn, left: left, right: right };
} }
@ -710,7 +707,7 @@ export class ECSCompiler extends Tokenizer {
let valtype : IntType = { dtype: 'int', lo: value, hi: value }; let valtype : IntType = { dtype: 'int', lo: value, hi: value };
return { valtype, value }; return { valtype, value };
case TokenType.Ident: case TokenType.Ident:
if (tok.str == 'NOT') { if (tok.str == 'not') {
let expr = this.parsePrimary(); let expr = this.parsePrimary();
let valtype : IntType = { dtype: 'int', lo: 0, hi: 1 }; let valtype : IntType = { dtype: 'int', lo: 0, hi: 1 };
return { valtype, op: 'lnot', expr: expr }; return { valtype, op: 'lnot', expr: expr };
@ -752,7 +749,7 @@ export class ECSCompiler extends Tokenizer {
if (!this.currentScope) throw this.compileError(`This operation only works inside of a scope.`); if (!this.currentScope) throw this.compileError(`This operation only works inside of a scope.`);
let atypes = this.em.archetypesMatching({ include: [component] }) let atypes = this.em.archetypesMatching({ include: [component] })
let entities = this.currentScope.entitiesMatching(atypes); let entities = this.currentScope.entitiesMatching(atypes);
return { entities, field } as EntitySetField; return { entities, field } as EntityFieldOp;
} }
// entity.field // entity.field
if (this.ifToken('.')) { if (this.ifToken('.')) {
@ -764,7 +761,7 @@ export class ECSCompiler extends Tokenizer {
let field = component.fields.find(f => f.name == ftok.str); let field = component.fields.find(f => f.name == ftok.str);
if (!field) throw this.compileError(`There is no "${ftok.str}" field in this entity.`); if (!field) throw this.compileError(`There is no "${ftok.str}" field in this entity.`);
let entities = [entity]; let entities = [entity];
return { entities, field } as EntitySetField; return { entities, field } as EntityFieldOp;
} }
let args : Expr[] = []; let args : Expr[] = [];
if (this.ifToken('(')) { if (this.ifToken('(')) {
@ -796,6 +793,22 @@ export class ECSCompiler extends Tokenizer {
parseExprList(): Expr[] { parseExprList(): Expr[] {
return this.parseList(this.parseExpr, ','); return this.parseList(this.parseExpr, ',');
} }
parseBlockStatement(): Statement {
let valtype : IntType = { dtype:'int', lo:0, hi: 0 }
if (this.peekToken().type == ECSTokenType.CodeFragment) {
return { valtype, code: this.parseCode() };
}
let cmd = this.expectTokens(['end','begin']);
if (cmd.str == 'begin') {
let stmts = [];
while (this.peekToken().str != 'end') {
stmts.push(this.parseBlockStatement());
}
this.expectToken('end');
return { valtype, stmts };
}
throw this.internalError();
}
} }
/// ///

View File

@ -78,6 +78,7 @@ export const SELECT_TYPE = ['once', 'foreach', 'join', 'with', 'if', 'select', '
export type SelectType = typeof SELECT_TYPE[number]; export type SelectType = typeof SELECT_TYPE[number];
// TODO?
export interface ActionContext { export interface ActionContext {
system: System system: System
scope: EntityScope | null scope: EntityScope | null
@ -112,20 +113,10 @@ export class CodePlaceholderNode extends ActionNode {
} }
} }
class QueryNode extends ActionNode {
}
class WrapperNode extends ActionNode {
}
class LoopNode extends ActionNode {
}
export interface ActionBase extends SourceLocated { export interface ActionBase extends SourceLocated {
select: SelectType; select: SelectType;
event: string; event: string;
text: string; expr: Statement;
critical?: boolean; critical?: boolean;
fitbytes?: number; fitbytes?: number;
} }
@ -204,18 +195,17 @@ export interface ForwardRef extends SourceLocated {
token: Token token: Token
} }
export type LExpr = IndOp | EntitySetField; export type LExpr = IndOp | EntityFieldOp;
export type ExprTypes = BinOp | UnOp | Literal | ForwardRef | LExpr; export type Statement = InlineCode | BlockExpr;
export type Expr = ExprTypes; // & SourceLocated; export type Expr = BinOp | UnOp | Literal | ForwardRef | LExpr | Statement;
export type Opcode = string; export type Opcode = string;
export type Value = DataValue;
export interface ExprBase extends SourceLocated { export interface ExprBase extends SourceLocated {
valtype: DataType; valtype: DataType;
} }
export interface Literal extends ExprBase { export interface Literal extends ExprBase {
value: Value; value: DataValue;
} }
export interface LiteralInt extends Literal { export interface LiteralInt extends Literal {
@ -239,11 +229,34 @@ export interface IndOp extends ExprBase {
args: Expr[]; args: Expr[];
} }
export interface EntitySetField extends ExprBase { export interface CondOp extends ExprBase {
cond: Expr;
iftrue?: Expr;
iffalse?: Expr;
}
export interface BlockExpr extends ExprBase {
loop?: boolean;
stmts: Expr[];
}
export interface BranchOp extends ExprBase {
branch: BlockExpr;
}
export interface EntityFieldOp extends ExprBase {
entities: Entity[]; entities: Entity[];
field: DataField; field: DataField;
} }
export interface EntityContextOp extends ExprBase {
entities: Entity[];
}
export interface InlineCode extends ExprBase {
code: string;
}
export function isLiteral(arg: Expr): arg is Literal { export function isLiteral(arg: Expr): arg is Literal {
return (arg as any).value != null; return (arg as any).value != null;
} }
@ -259,6 +272,12 @@ export function isBinOp(arg: Expr): arg is BinOp {
export function isUnOp(arg: Expr): arg is UnOp { export function isUnOp(arg: Expr): arg is UnOp {
return (arg as any).op != null && (arg as any).expr != null; return (arg as any).op != null && (arg as any).expr != null;
} }
export function isBlockStmt(arg: Expr): arg is BlockExpr {
return (arg as any).stmts != null;
}
export function isInlineCode(arg: Expr): arg is InlineCode {
return (arg as any).code != null;
}
/// DIALECT /// DIALECT
@ -796,7 +815,7 @@ class ActionEval {
return code; return code;
} }
private getCodeAndProps(action: Action) { private getCodeAndProps(action: Action) {
let code = action.text; let code = this.exprToCode(action.expr);
let props: { [name: string]: string } = {}; let props: { [name: string]: string } = {};
if (action.select != 'once') { if (action.select != 'once') {
// TODO: detect cycles // TODO: detect cycles
@ -1125,6 +1144,15 @@ class ActionEval {
if (code.split('\n ').length >= 4) return true; // TODO: :^/ if (code.split('\n ').length >= 4) return true; // TODO: :^/
return false; return false;
} }
exprToCode(expr: Expr) : string {
if (isBlockStmt(expr)) {
return expr.stmts.map(node => this.exprToCode(node)).join('\n');
}
if (isInlineCode(expr)) {
return expr.code;
}
throw new ECSError(`cannot convert expression to code`, expr);
}
} }
class EventCodeStats { class EventCodeStats {