diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 6d9a63af..b3b96948 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,7 +1,7 @@ import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; -import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; +import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, ECSError, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; export enum ECSTokenType { Ellipsis = 'ellipsis', @@ -129,14 +129,18 @@ export class ECSCompiler extends Tokenizer { if (this.peekToken().str == '[') { return { dtype: 'ref', query: this.parseQuery() } as RefType; } - if (this.peekToken().str == 'array') { + if (this.ifToken('array')) { let index : IntType | undefined = undefined; - this.expectToken('array'); if (this.peekToken().type == ECSTokenType.Integer) { index = this.parseDataType() as IntType; } this.expectToken('of'); - return { dtype: 'array', index, elem: this.parseDataType() } as ArrayType; + let elem = this.parseDataType(); + let baseoffset; + if (this.ifToken('baseoffset')) { + baseoffset = this.expectInteger(); + } + return { dtype: 'array', index, elem, baseoffset } as ArrayType; } this.internalError(); throw new Error(); } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index fdaec5d6..83ee871d 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -58,6 +58,7 @@ how to avoid cycle crossing for critical code and data? */ +import { data } from "jquery"; import { SourceLocated, SourceLocation } from "../workertypes"; export class ECSError extends Error { @@ -153,6 +154,7 @@ export interface ArrayType { dtype: 'array' elem: DataType index?: DataType + baseoffset?: number } export interface RefType { @@ -651,8 +653,18 @@ class ActionEval { let bitofs = parseInt(args[1] || '0'); return this.generateCodeForField(fieldName, bitofs, canwrite); } + __base(args: string[]) { + // TODO: refactor into generateCode.. + let fieldName = args[0]; + let bitofs = parseInt(args[1] || '0'); + let component = this.em.singleComponentWithFieldName(this.qr.atypes, fieldName, this.action); + let field = component.fields.find(f => f.name == fieldName); + if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, this.action); + return this.dialect.fieldsymbol(component, field, bitofs); + } __index(args: string[]) { - let ident = args[0]; // TODO? + // TODO: check select type and if we actually have an index... + let ident = args[0]; if (this.entities.length == 1) { return this.dialect.absolute(ident); } else { @@ -727,6 +739,7 @@ class ActionEval { // find archetypes let field = component.fields.find(f => f.name == fieldName); if (field == null) throw new ECSError(`no field named "${fieldName}" in component`, action); + let ident = this.dialect.fieldsymbol(component, field, bitofs); // see if all entities have the same constant value // TODO: should be done somewhere else? let constValues = new Set(); @@ -753,7 +766,6 @@ class ActionEval { let range = this.scope.bss.getFieldRange(component, fieldName) || this.scope.rodata.getFieldRange(component, fieldName); if (!range) throw new ECSError(`couldn't find field for ${component.name}:${fieldName}, maybe no entities?`); // TODO // TODO: dialect - let ident = this.dialect.fieldsymbol(component, field, bitofs); let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative? if (baseLookup) { return this.dialect.absolute(ident); @@ -940,24 +952,27 @@ export class EntityScope implements SourceLocated { // is it a bounded array? (TODO) if (f.dtype == 'array' && f.index) { let datasym = this.dialect.datasymbol(c, f, e.id); - let offset = this.bss.allocateBytes(datasym, getFieldLength(f.index)); + let databytes = getFieldLength(f.index); + let offset = this.bss.allocateBytes(datasym, databytes); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); + if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; } } else { // this is a constant // is it a byte array? - if (v instanceof Uint8Array) { + if (v instanceof Uint8Array && f.dtype == 'array') { let datasym = this.dialect.datasymbol(c, f, e.id); segment.allocateInitData(datasym, v); let ptrlosym = this.dialect.fieldsymbol(c, f, 0); let ptrhisym = this.dialect.fieldsymbol(c, f, 8); let loofs = segment.allocateBytes(ptrlosym, entcount); let hiofs = segment.allocateBytes(ptrhisym, entcount); + if (f.baseoffset) datasym = `(${datasym}+${f.baseoffset})`; segment.initdata[loofs + e.id - range.elo] = { symbol: datasym, bitofs: 0 }; segment.initdata[hiofs + e.id - range.elo] = { symbol: datasym, bitofs: 8 }; // TODO: } else if (v instanceof Uint16Array) { diff --git a/src/common/tokenizer.ts b/src/common/tokenizer.ts index bb52f568..1dd1c5ba 100644 --- a/src/common/tokenizer.ts +++ b/src/common/tokenizer.ts @@ -160,6 +160,9 @@ export class Tokenizer { let tok = this.lasttoken = (this.tokens.shift() || this.eof); return tok; } + ifToken(match: string): Token | undefined { + if (this.peekToken().str == match) return this.consumeToken(); + } expectToken(str: string, msg?: string): Token { let tok = this.consumeToken(); let tokstr = tok.str;