mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-05-12 21:06:10 +00:00
ecs: field, ref type checking, start cmd
This commit is contained in:
parent
7767608595
commit
a444de693b
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,13 +1,13 @@
|
||||
*~
|
||||
node_modules
|
||||
./local/
|
||||
./tests_output/
|
||||
./test/output/
|
||||
/local/
|
||||
/tests_output/
|
||||
/test/output/
|
||||
.DS_Store
|
||||
./tmp/
|
||||
./web/
|
||||
./release/
|
||||
./gen/
|
||||
/tmp/
|
||||
/web/
|
||||
/release/
|
||||
/gen/
|
||||
config.js
|
||||
chromedriver.log
|
||||
nightwatch.conf.js
|
||||
|
@ -143,3 +143,11 @@ banks need to duplicate code and/or rodata
|
||||
- don't split critical code across banks
|
||||
need bank trampoline macro
|
||||
nested scopes for game modes? (title / demo / play)
|
||||
|
||||
critical data fields
|
||||
if accessed in critical section, make critical
|
||||
ignore arrays that aren't referenced
|
||||
|
||||
use DASM for multipass?
|
||||
|
||||
default values for component fields
|
||||
|
@ -25,8 +25,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
debuginfo = false;
|
||||
|
||||
constructor(
|
||||
public readonly em: EntityManager)
|
||||
{
|
||||
public readonly em: EntityManager) {
|
||||
super();
|
||||
//this.includeEOL = true;
|
||||
this.setTokenRules([
|
||||
@ -78,6 +77,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
comp.parseFile(text, path);
|
||||
} catch (e) {
|
||||
for (var err of comp.errors) this.errors.push(err);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,6 +105,8 @@ export class ECSCompiler extends Tokenizer {
|
||||
if (tok.str == 'demo') {
|
||||
let scope = this.parseScope();
|
||||
scope.isDemo = true;
|
||||
// TODO: make required
|
||||
if (this.peekToken().str == 'demo') this.expectToken('demo');
|
||||
return scope;
|
||||
}
|
||||
if (tok.str == 'comment') {
|
||||
@ -398,25 +400,37 @@ export class ECSCompiler extends Tokenizer {
|
||||
let cmd = cmd2; // put in scope
|
||||
if (cmd == 'var') cmd = 'init'; // TODO: remove?
|
||||
if (cmd == 'init' || cmd == 'const') {
|
||||
this.parseInitConst(cmd, scope, entity);
|
||||
} else if (cmd == 'decode') {
|
||||
this.parseDecode(scope, entity);
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
parseInitConst(cmd: string, scope: EntityScope, entity: Entity) {
|
||||
// TODO: check data types
|
||||
let name = this.expectIdent().str;
|
||||
let { c, f } = this.getEntityField(entity, name);
|
||||
let symtype = this.currentScope.isConstOrInit(c, name);
|
||||
let symtype = scope.isConstOrInit(c, name);
|
||||
if (symtype && symtype != cmd)
|
||||
this.compileError(`I can't mix const and init values for a given field in a scope.`);
|
||||
this.expectToken('=');
|
||||
let valueOrRef = this.parseDataValue(f);
|
||||
if ((valueOrRef as ForwardRef).token != null) {
|
||||
this.deferred.push(() => {
|
||||
this.lasttoken = (valueOrRef as ForwardRef).token; // for errors
|
||||
let refvalue = this.resolveEntityRef(scope, valueOrRef as ForwardRef);
|
||||
if (cmd == 'const') scope.setConstValue(entity, c, name, refvalue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, c, name, refvalue);
|
||||
if (cmd == 'const') scope.setConstValue(entity, c, f, refvalue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, c, f, refvalue);
|
||||
});
|
||||
} else {
|
||||
if (cmd == 'const') scope.setConstValue(entity, c, name, valueOrRef as DataValue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, c, name, valueOrRef as DataValue);
|
||||
if (cmd == 'const') scope.setConstValue(entity, c, f, valueOrRef as DataValue);
|
||||
if (cmd == 'init') scope.setInitValue(entity, c, f, valueOrRef as DataValue);
|
||||
}
|
||||
} else if (cmd == 'decode') {
|
||||
}
|
||||
|
||||
parseDecode(scope: EntityScope, entity: Entity) {
|
||||
let decoderid = this.expectIdent().str;
|
||||
let codetok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
||||
let code = codetok.str;
|
||||
@ -431,12 +445,9 @@ export class ECSCompiler extends Tokenizer {
|
||||
}
|
||||
for (let entry of Object.entries(result.properties)) {
|
||||
let { c, f } = this.getEntityField(entity, entry[0]);
|
||||
scope.setConstValue(entity, c, f.name, entry[1] as DataValue);
|
||||
scope.setConstValue(entity, c, f, entry[1] as DataValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
getEntityField(e: Entity, name: string): ComponentFieldPair {
|
||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||
@ -526,8 +537,7 @@ export class ECSCompiler extends Tokenizer {
|
||||
|
||||
export class ECSActionCompiler extends Tokenizer {
|
||||
constructor(
|
||||
public readonly context: ActionContext)
|
||||
{
|
||||
public readonly context: ActionContext) {
|
||||
super();
|
||||
this.setTokenRules([
|
||||
{ type: ECSTokenType.Placeholder, regex: /\{\{.*?\}\}/ },
|
||||
|
@ -309,9 +309,12 @@ export class Dialect_CA65 {
|
||||
return `.scope ${name}`
|
||||
}
|
||||
endScope(name: string) {
|
||||
return `.endscope\n${name}__Start = ${name}::__Start`
|
||||
return `.endscope\n${this.scopeSymbol(name)} = ${name}::__Start`
|
||||
// TODO: scope__start = scope::start
|
||||
}
|
||||
scopeSymbol(name: string) {
|
||||
return `${name}__Start`;
|
||||
}
|
||||
align(value: number) {
|
||||
return `.align ${value}`;
|
||||
}
|
||||
@ -365,6 +368,9 @@ export class Dialect_CA65 {
|
||||
call(symbol: string) {
|
||||
return ` jsr ${symbol}`;
|
||||
}
|
||||
jump(symbol: string) {
|
||||
return ` jmp ${symbol}`;
|
||||
}
|
||||
return() {
|
||||
return ' rts';
|
||||
}
|
||||
@ -662,6 +668,8 @@ class ActionEval {
|
||||
else state.xreg = new IndexRegister(this.scope, this.qr);
|
||||
break;
|
||||
case 'join':
|
||||
// TODO: Joins don't work in superman (arrays offset?)
|
||||
// ignore the join query, use the ref
|
||||
if (state.xreg || state.yreg) throw new ECSError('no free index registers for join', this.action);
|
||||
this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join);
|
||||
state.xreg = new IndexRegister(this.scope, this.jr);
|
||||
@ -882,6 +890,10 @@ class ActionEval {
|
||||
//this.used.add(`arg_${argindex}_${argvalue}`);
|
||||
return argvalue;
|
||||
}
|
||||
__start(args: string[]) {
|
||||
let startSymbol = this.dialect.scopeSymbol(args[0]);
|
||||
return this.dialect.jump(startSymbol);
|
||||
}
|
||||
wrapCodeInLoop(code: string, action: ActionWithQuery, ents: Entity[], joinfield?: ComponentFieldPair): string {
|
||||
// TODO: check ents
|
||||
// TODO: check segment bounds
|
||||
@ -976,9 +988,8 @@ class ActionEval {
|
||||
if (baseLookup) {
|
||||
return this.dialect.absolute(ident);
|
||||
} else if (entities.length == 1) {
|
||||
let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative?
|
||||
if (entityLookup)
|
||||
eidofs = entities[0].id - range.elo;
|
||||
// TODO: qr or this.entites?
|
||||
let eidofs = entities[0].id - range.elo; // TODO: negative?
|
||||
return this.dialect.absolute(ident, eidofs);
|
||||
} else {
|
||||
let ir;
|
||||
@ -1298,15 +1309,17 @@ export class EntityScope implements SourceLocated {
|
||||
return this.bss.getFieldRange(c, fn) || this.rodata.getFieldRange(c, fn);
|
||||
}
|
||||
// TODO: check type/range of value
|
||||
setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) {
|
||||
this.setConstInitValue(e, component, fieldName, value, 'const');
|
||||
setConstValue(e: Entity, component: ComponentType, field: DataField, value: DataValue) {
|
||||
this.setConstInitValue(e, component, field, value, 'const');
|
||||
}
|
||||
setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) {
|
||||
this.setConstInitValue(e, component, fieldName, value, 'init');
|
||||
setInitValue(e: Entity, component: ComponentType, field: DataField, value: DataValue) {
|
||||
this.setConstInitValue(e, component, field, value, 'init');
|
||||
}
|
||||
setConstInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue,
|
||||
type: 'const'|'init') {
|
||||
this.em.singleComponentWithFieldName([e.etype], fieldName, e);
|
||||
setConstInitValue(e: Entity, component: ComponentType, field: DataField, value: DataValue,
|
||||
type: 'const'|'init')
|
||||
{
|
||||
this.checkValueType(field, value);
|
||||
let fieldName = field.name;
|
||||
let cfname = mksymbol(component, fieldName);
|
||||
let ecfname = mkscopesymbol(this, component, fieldName);
|
||||
if (e.consts[cfname] !== undefined) throw new ECSError(`"${fieldName}" is already defined as a constant`, e);
|
||||
@ -1318,6 +1331,24 @@ export class EntityScope implements SourceLocated {
|
||||
isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' {
|
||||
return this.fieldtypes[mksymbol(component, fieldName)];
|
||||
}
|
||||
checkValueType(field: DataField, value: DataValue) {
|
||||
if (field.dtype == 'array') {
|
||||
if (!(value instanceof Uint8Array))
|
||||
throw new ECSError(`This "${field.name}" value should be an array.`);
|
||||
} else if (typeof value !== 'number') {
|
||||
throw new ECSError(`This "${field.name}" ${field.dtype} value should be an number.`);
|
||||
} else {
|
||||
if (field.dtype == 'int') {
|
||||
if (value < field.lo || value > field.hi)
|
||||
throw new ECSError(`This "${field.name}" value is out of range, should be between ${field.lo} and ${field.hi}.`);
|
||||
} else if (field.dtype == 'ref') {
|
||||
// TODO: allow override if number
|
||||
let eset = new EntitySet(this, field.query);
|
||||
if (value < 0 || value >= eset.entities.length)
|
||||
throw new ECSError(`This "${field.name}" value is out of range for this ref type.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateCodeForEvent(event: string, args?: string[], codelabel?: string): string {
|
||||
// find systems that respond to event
|
||||
// and have entities in this scope
|
||||
|
@ -159,6 +159,7 @@ end
|
||||
entity NullShape [Bitmap,Colormap]
|
||||
decode vcs_sprite
|
||||
---
|
||||
........ 00
|
||||
---
|
||||
end
|
||||
|
||||
@ -504,7 +505,7 @@ x.x.x.x.x.x.x.x.x.x. .. 06 ..
|
||||
var sprite = #Superdude
|
||||
end
|
||||
entity Slot1 [SpriteSlot]
|
||||
var sprite = $ff
|
||||
var sprite = 0 // $ff
|
||||
end
|
||||
|
||||
using VersatilePlayfield with #Superdude.room
|
||||
|
@ -60,8 +60,9 @@ Bitmap_bitmapdata_b8:
|
||||
.byte >(Bitmap_bitmapdata_e2_b0+31)
|
||||
.byte >(Bitmap_bitmapdata_e3_b0+31)
|
||||
Bitmap_bitmapdata_e1_b0:
|
||||
.byte 0
|
||||
Bitmap_height_b0:
|
||||
.byte 255
|
||||
.byte 0
|
||||
.byte 17
|
||||
.byte 27
|
||||
Colormap_colormapdata_b0:
|
||||
@ -73,6 +74,7 @@ Colormap_colormapdata_b8:
|
||||
.byte >(Colormap_colormapdata_e2_b0+31)
|
||||
.byte >(Colormap_colormapdata_e3_b0+31)
|
||||
Colormap_colormapdata_e1_b0:
|
||||
.byte 0
|
||||
Bitmap_bitmapdata_e2_b0:
|
||||
.byte 128
|
||||
.byte 192
|
||||
@ -622,7 +624,7 @@ Main__INITDATA:
|
||||
.byte 60
|
||||
.byte 90
|
||||
.byte 0
|
||||
.byte 255
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user