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:
parent
6ca0a707e1
commit
f7099b8e8f
@ -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',
|
||||
];
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user