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 { SourceLocated, SourceLocation } from "../workertypes";
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 {
Ellipsis = 'ellipsis',
@ -349,10 +349,9 @@ export class ECSCompiler extends Tokenizer {
tempbytes = this.parseIntegerConstant();
}
let system: System = { name, tempbytes, actions: [] };
let context: ActionContext = { scope: null, system };
let text = this.parseCode(context);
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);
return system;
}
@ -384,13 +383,11 @@ export class ECSCompiler extends Tokenizer {
if (this.ifToken('fit')) {
fitbytes = this.parseIntegerConstant();
}
let context: ActionContext = { scope: null, system };
// parse --- code ---
let text = this.parseCode(context);
let direction = undefined;
if (modifiers['asc']) direction = 'asc';
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;
return action as ActionWithJoin;
}
@ -441,7 +438,7 @@ export class ECSCompiler extends Tokenizer {
return this.parseList(this.parseEventName, ",");
}
parseCode(context: ActionContext): string { // TODOActionNode[] {
parseCode(): string { // TODOActionNode[] {
// TODO: add $loc
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
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);
code = lines.join('\n');
let acomp = new ECSActionCompiler(context);
let nodes = acomp.parseFile(code, this.path);
//let acomp = new ECSActionCompiler(context);
//let nodes = acomp.parseFile(code, this.path);
// TODO: return nodes
return code;
}
@ -694,8 +691,8 @@ export class ECSCompiler extends Tokenizer {
}
var opfn = getOperator(op.str).f;
// use logical operators instead of bitwise?
if (op.str == 'AND') opfn = 'land';
if (op.str == 'OR') opfn = 'lor';
if (op.str == 'and') opfn = 'land';
if (op.str == 'or') opfn = 'lor';
var valtype = this.exprTypeForOp(opfn, left, right, op);
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 };
return { valtype, value };
case TokenType.Ident:
if (tok.str == 'NOT') {
if (tok.str == 'not') {
let expr = this.parsePrimary();
let valtype : IntType = { dtype: 'int', lo: 0, hi: 1 };
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.`);
let atypes = this.em.archetypesMatching({ include: [component] })
let entities = this.currentScope.entitiesMatching(atypes);
return { entities, field } as EntitySetField;
return { entities, field } as EntityFieldOp;
}
// entity.field
if (this.ifToken('.')) {
@ -764,7 +761,7 @@ export class ECSCompiler extends Tokenizer {
let field = component.fields.find(f => f.name == ftok.str);
if (!field) throw this.compileError(`There is no "${ftok.str}" field in this entity.`);
let entities = [entity];
return { entities, field } as EntitySetField;
return { entities, field } as EntityFieldOp;
}
let args : Expr[] = [];
if (this.ifToken('(')) {
@ -796,6 +793,22 @@ export class ECSCompiler extends Tokenizer {
parseExprList(): Expr[] {
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];
// TODO?
export interface ActionContext {
system: System
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 {
select: SelectType;
event: string;
text: string;
expr: Statement;
critical?: boolean;
fitbytes?: number;
}
@ -204,18 +195,17 @@ export interface ForwardRef extends SourceLocated {
token: Token
}
export type LExpr = IndOp | EntitySetField;
export type ExprTypes = BinOp | UnOp | Literal | ForwardRef | LExpr;
export type Expr = ExprTypes; // & SourceLocated;
export type LExpr = IndOp | EntityFieldOp;
export type Statement = InlineCode | BlockExpr;
export type Expr = BinOp | UnOp | Literal | ForwardRef | LExpr | Statement;
export type Opcode = string;
export type Value = DataValue;
export interface ExprBase extends SourceLocated {
valtype: DataType;
}
export interface Literal extends ExprBase {
value: Value;
value: DataValue;
}
export interface LiteralInt extends Literal {
@ -239,11 +229,34 @@ export interface IndOp extends ExprBase {
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[];
field: DataField;
}
export interface EntityContextOp extends ExprBase {
entities: Entity[];
}
export interface InlineCode extends ExprBase {
code: string;
}
export function isLiteral(arg: Expr): arg is Literal {
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 {
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
@ -796,7 +815,7 @@ class ActionEval {
return code;
}
private getCodeAndProps(action: Action) {
let code = action.text;
let code = this.exprToCode(action.expr);
let props: { [name: string]: string } = {};
if (action.select != 'once') {
// TODO: detect cycles
@ -1125,6 +1144,15 @@ class ActionEval {
if (code.split('\n ').length >= 4) return true; // TODO: :^/
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 {