1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-12-25 15:30:03 +00:00

ecs: enum keyword, entity.field constants

This commit is contained in:
Steven Hugg 2022-02-22 12:33:57 -06:00
parent 6ca0a707e1
commit f7099b8e8f
3 changed files with 81 additions and 3 deletions

View File

@ -19,6 +19,7 @@
var directives_list = [
'end', 'component', 'system', 'entity', 'scope', 'using', 'demo', 'decode', 'resource',
'const', 'locals', 'var',
'enum', 'default', 'array', 'baseoffset', 'critical',
'on', 'do', 'emit', 'limit',
'once', 'foreach', 'with', 'join', 'if',
];

View File

@ -139,11 +139,16 @@ export class ECSCompiler extends Tokenizer {
let lo = this.expectInteger();
this.expectToken('..');
let hi = this.expectInteger();
this.checkLowerLimit(lo, -0x80000000, "lower int range");
this.checkUpperLimit(hi, 0x7fffffff, "upper int range");
this.checkUpperLimit(hi-lo, 0xffffffff, "int range");
this.checkLowerLimit(hi, lo, "int range");
// TODO: use default value?
let defvalue;
if (this.ifToken('default')) {
defvalue = this.expectInteger();
}
// TODO: check types
return { dtype: 'int', lo, hi, defvalue } as IntType;
}
if (this.peekToken().str == '[') {
@ -159,17 +164,67 @@ export class ECSCompiler extends Tokenizer {
let baseoffset;
if (this.ifToken('baseoffset')) {
baseoffset = this.expectInteger();
this.checkLowerLimit(baseoffset, -32768, "base offset");
this.checkUpperLimit(baseoffset, 32767, "base offset");
}
return { dtype: 'array', index, elem, baseoffset } as ArrayType;
}
if (this.ifToken('enum')) {
this.expectToken('[');
let enumtoks = this.parseList(this.parseEnumIdent, ',');
this.expectToken(']');
if (enumtoks.length == 0) this.compileError(`must define at least one enum`);
let lo = 0;
let hi = enumtoks.length-1;
this.checkLowerLimit(hi, 0, "enum count");
this.checkUpperLimit(hi, 255, "enum count");
let enums : {[name:string]:number} = {};
for (let i=0; i<=hi; i++)
enums[enumtoks[i].str] = i;
// TODO: use default value?
let defvalue;
if (this.ifToken('default')) {
defvalue = this.expectInteger();
}
return { dtype: 'int', lo, hi, defvalue, enums } as IntType;
}
this.compileError(`I expected a data type here.`); throw new Error();
}
parseEnumIdent() {
let tok = this.expectTokenTypes([TokenType.Ident]);
return tok;
}
parseEnumValue(tok: Token, field: IntType) {
if (!field.enums) throw new ECSError(`field is not an enum`);
let value = field.enums[tok.str];
if (value == null) throw new ECSError(`unknown enum "${tok.str}"`);
return value;
}
parseDataValue(field: DataField): DataValue | ForwardRef {
let tok = this.peekToken();
if (tok.type == 'integer') {
if (tok.type == ECSTokenType.Integer) {
return this.expectInteger();
}
if (tok.type == TokenType.Ident && field.dtype == 'int') {
return this.parseEnumValue(this.consumeToken(), field);
}
if (tok.type == TokenType.Ident) {
let entity = this.currentScope?.getEntityByName(tok.str);
if (!entity)
this.compileError('no entity named "${tok.str}"');
else {
this.consumeToken();
this.expectToken('.');
let fieldName = this.expectIdent().str;
let constValue = this.currentScope?.getConstValue(entity, fieldName);
if (constValue == null)
throw new ECSError(`"${fieldName}" is not defined as a constant`, entity);
else
return constValue;
}
}
if (tok.str == '[') {
// TODO: 16-bit?
return new Uint8Array(this.parseDataArray());
@ -303,7 +358,10 @@ export class ECSCompiler extends Tokenizer {
q.exclude.push(cref);
} else if (prefix.str == '#') {
const scope = this.currentScope;
if (scope == null) { this.internalError(); throw new Error(); }
if (scope == null) {
this.compileError('You can only reference specific entities inside of a scope.');
throw new Error();
}
let eref = this.parseEntityForwardRef();
this.deferred.push(() => {
let refvalue = this.resolveEntityRef(scope, eref);
@ -537,8 +595,17 @@ export class ECSCompiler extends Tokenizer {
this.exportToFile(src);
return src.toString();
}
checkUpperLimit(value: number, upper: number, what: string) {
if (value > upper) this.compileError(`This ${what} is too high; must be ${upper} or less`);
}
checkLowerLimit(value: number, lower: number, what: string) {
if (value < lower) this.compileError(`This ${what} is too low; must be ${lower} or more`);
}
}
///
export class ECSActionCompiler extends Tokenizer {
constructor(
public readonly context: ActionContext) {

View File

@ -157,6 +157,7 @@ export interface IntType {
lo: number
hi: number
defvalue?: number
enums?: { [name: string] : number }
}
export interface ArrayType {
@ -415,6 +416,7 @@ class DataSegment {
this.ofs2sym.set(ofs, []);
this.ofs2sym.get(ofs)?.push(name);
}
// TODO: ordering should not matter, but it does
findExistingInitData(bytes: Uint8Array) {
for (let i=0; i<this.size - bytes.length; i++) {
for (var j=0; j<bytes.length; j++) {
@ -475,7 +477,9 @@ class UninitDataSegment extends DataSegment {
class ConstDataSegment extends DataSegment {
}
// TODO: none of this makes sense
function getFieldBits(f: IntType) {
//let n = Math.abs(f.lo) + f.hi + 1;
let n = f.hi - f.lo + 1;
return Math.ceil(Math.log2(n));
}
@ -1340,6 +1344,11 @@ export class EntityScope implements SourceLocated {
isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' {
return this.fieldtypes[mksymbol(component, fieldName)];
}
getConstValue(entity: Entity, fieldName: string) {
let component = this.em.singleComponentWithFieldName([entity.etype], fieldName, entity);
let cfname = mksymbol(component, fieldName);
return entity.consts[cfname];
}
checkFieldValue(field: DataField, value: DataValue) {
if (field.dtype == 'array') {
if (!(value instanceof Uint8Array))
@ -1671,9 +1680,10 @@ export class EntityManager {
}
singleComponentWithFieldName(atypes: EntityArchetype[], fieldName: string, where: SourceLocated) {
let cfpairs = this.name2cfpairs[fieldName];
if (!cfpairs) throw new ECSError(`cannot find field named "${fieldName}"`, where);
let filtered = cfpairs.filter(cf => atypes.find(a => a.components.includes(cf.c)));
if (filtered.length == 0) {
throw new ECSError(`cannot find component with field "${fieldName}"`, where);
throw new ECSError(`cannot find component with field "${fieldName}" in this context`, where);
}
if (filtered.length > 1) {
throw new ECSError(`ambiguous field name "${fieldName}"`, where);