mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-05-13 12:38:19 +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
|
node_modules
|
||||||
./local/
|
/local/
|
||||||
./tests_output/
|
/tests_output/
|
||||||
./test/output/
|
/test/output/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
./tmp/
|
/tmp/
|
||||||
./web/
|
/web/
|
||||||
./release/
|
/release/
|
||||||
./gen/
|
/gen/
|
||||||
config.js
|
config.js
|
||||||
chromedriver.log
|
chromedriver.log
|
||||||
nightwatch.conf.js
|
nightwatch.conf.js
|
||||||
|
@ -143,3 +143,11 @@ banks need to duplicate code and/or rodata
|
|||||||
- don't split critical code across banks
|
- don't split critical code across banks
|
||||||
need bank trampoline macro
|
need bank trampoline macro
|
||||||
nested scopes for game modes? (title / demo / play)
|
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;
|
debuginfo = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly em: EntityManager)
|
public readonly em: EntityManager) {
|
||||||
{
|
|
||||||
super();
|
super();
|
||||||
//this.includeEOL = true;
|
//this.includeEOL = true;
|
||||||
this.setTokenRules([
|
this.setTokenRules([
|
||||||
@ -78,6 +77,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
comp.parseFile(text, path);
|
comp.parseFile(text, path);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
for (var err of comp.errors) this.errors.push(err);
|
for (var err of comp.errors) this.errors.push(err);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,12 +99,14 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
}
|
}
|
||||||
if (tok.str == 'import') {
|
if (tok.str == 'import') {
|
||||||
let tok = this.expectTokenTypes([ECSTokenType.QuotedString]);
|
let tok = this.expectTokenTypes([ECSTokenType.QuotedString]);
|
||||||
let path = tok.str.substring(1, tok.str.length-1);
|
let path = tok.str.substring(1, tok.str.length - 1);
|
||||||
return this.importFile(path);
|
return this.importFile(path);
|
||||||
}
|
}
|
||||||
if (tok.str == 'demo') {
|
if (tok.str == 'demo') {
|
||||||
let scope = this.parseScope();
|
let scope = this.parseScope();
|
||||||
scope.isDemo = true;
|
scope.isDemo = true;
|
||||||
|
// TODO: make required
|
||||||
|
if (this.peekToken().str == 'demo') this.expectToken('demo');
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
if (tok.str == 'comment') {
|
if (tok.str == 'comment') {
|
||||||
@ -143,7 +145,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
return { dtype: 'ref', query: this.parseQuery() } as RefType;
|
return { dtype: 'ref', query: this.parseQuery() } as RefType;
|
||||||
}
|
}
|
||||||
if (this.ifToken('array')) {
|
if (this.ifToken('array')) {
|
||||||
let index : IntType | undefined = undefined;
|
let index: IntType | undefined = undefined;
|
||||||
if (this.peekToken().type == ECSTokenType.Integer) {
|
if (this.peekToken().type == ECSTokenType.Integer) {
|
||||||
index = this.parseDataType() as IntType;
|
index = this.parseDataType() as IntType;
|
||||||
}
|
}
|
||||||
@ -158,7 +160,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
this.compileError(`I expected a data type here.`); throw new Error();
|
this.compileError(`I expected a data type here.`); throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseDataValue(field: DataField) : DataValue | ForwardRef {
|
parseDataValue(field: DataField): DataValue | ForwardRef {
|
||||||
let tok = this.peekToken();
|
let tok = this.peekToken();
|
||||||
if (tok.type == 'integer') {
|
if (tok.type == 'integer') {
|
||||||
return this.expectInteger();
|
return this.expectInteger();
|
||||||
@ -175,7 +177,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
this.compileError(`I expected a ${field.dtype} here.`); throw new Error();
|
this.compileError(`I expected a ${field.dtype} here.`); throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseEntityForwardRef(reftype?: RefType) : ForwardRef {
|
parseEntityForwardRef(reftype?: RefType): ForwardRef {
|
||||||
let token = this.expectIdent();
|
let token = this.expectIdent();
|
||||||
return { reftype, token };
|
return { reftype, token };
|
||||||
}
|
}
|
||||||
@ -189,7 +191,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
|
|
||||||
expectInteger(): number {
|
expectInteger(): number {
|
||||||
let s = this.consumeToken().str;
|
let s = this.consumeToken().str;
|
||||||
let i : number;
|
let i: number;
|
||||||
if (s.startsWith('$'))
|
if (s.startsWith('$'))
|
||||||
i = parseInt(s.substring(1), 16); // hex $...
|
i = parseInt(s.substring(1), 16); // hex $...
|
||||||
else if (s.startsWith('%'))
|
else if (s.startsWith('%'))
|
||||||
@ -205,7 +207,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
let actions: Action[] = [];
|
let actions: Action[] = [];
|
||||||
let system: System = { name, actions };
|
let system: System = { name, actions };
|
||||||
let cmd;
|
let cmd;
|
||||||
while ((cmd = this.expectTokens(['on','locals','end']).str) != 'end') {
|
while ((cmd = this.expectTokens(['on', 'locals', 'end']).str) != 'end') {
|
||||||
if (cmd == 'on') {
|
if (cmd == 'on') {
|
||||||
let action = this.annotate(() => this.parseAction(system));
|
let action = this.annotate(() => this.parseAction(system));
|
||||||
actions.push(action);
|
actions.push(action);
|
||||||
@ -225,11 +227,11 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
this.consumeToken();
|
this.consumeToken();
|
||||||
tempbytes = this.expectInteger();
|
tempbytes = this.expectInteger();
|
||||||
}
|
}
|
||||||
let system : System = { name, tempbytes, actions: [] };
|
let system: System = { name, tempbytes, actions: [] };
|
||||||
let context : ActionContext = { scope: null, system };
|
let context: ActionContext = { scope: null, system };
|
||||||
let text = this.parseCode(context);
|
let text = this.parseCode(context);
|
||||||
let select : SelectType = 'once';
|
let select: SelectType = 'once';
|
||||||
let action : Action = { text, event: name, select };
|
let action: Action = { text, event: name, select };
|
||||||
system.actions.push(action);
|
system.actions.push(action);
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
@ -240,7 +242,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
this.expectToken('do');
|
this.expectToken('do');
|
||||||
// TODO: include modifiers in error msg
|
// TODO: include modifiers in error msg
|
||||||
const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check?
|
const select = this.expectTokens(SELECT_TYPE).str as SelectType; // TODO: type check?
|
||||||
const all_modifiers = ['critical','asc','desc']; // TODO
|
const all_modifiers = ['critical', 'asc', 'desc']; // TODO
|
||||||
let query = undefined;
|
let query = undefined;
|
||||||
let join = undefined;
|
let join = undefined;
|
||||||
if (select == 'once') {
|
if (select == 'once') {
|
||||||
@ -261,7 +263,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
if (this.ifToken('fit')) {
|
if (this.ifToken('fit')) {
|
||||||
fitbytes = this.expectInteger();
|
fitbytes = this.expectInteger();
|
||||||
}
|
}
|
||||||
let context : ActionContext = { scope: null, system };
|
let context: ActionContext = { scope: null, system };
|
||||||
// parse --- code ---
|
// parse --- code ---
|
||||||
let text = this.parseCode(context);
|
let text = this.parseCode(context);
|
||||||
let direction = undefined;
|
let direction = undefined;
|
||||||
@ -319,7 +321,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
parseCode(context: ActionContext): string { // TODOActionNode[] {
|
parseCode(context: ActionContext): string { // TODOActionNode[] {
|
||||||
// TODO: add $loc
|
// TODO: add $loc
|
||||||
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
let tok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
||||||
let code = tok.str.substring(3, tok.str.length-3);
|
let code = tok.str.substring(3, tok.str.length - 3);
|
||||||
// TODO: add after parsing maybe?
|
// TODO: add after parsing maybe?
|
||||||
let lines = code.split('\n');
|
let lines = code.split('\n');
|
||||||
if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line);
|
if (this.debuginfo) this.addDebugInfo(lines, tok.$loc.line);
|
||||||
@ -333,13 +335,13 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
|
|
||||||
addDebugInfo(lines: string[], startline: number) {
|
addDebugInfo(lines: string[], startline: number) {
|
||||||
const re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines
|
const re = /^\s*(;|\/\/|$)/; // ignore comments and blank lines
|
||||||
for (let i=0; i<lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].match(re))
|
if (!lines[i].match(re))
|
||||||
lines[i] = this.em.dialect.debug_line(this.path, startline+i) + '\n' + lines[i];
|
lines[i] = this.em.dialect.debug_line(this.path, startline + i) + '\n' + lines[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseScope() : EntityScope {
|
parseScope(): EntityScope {
|
||||||
let name = this.expectIdent().str;
|
let name = this.expectIdent().str;
|
||||||
let scope = this.em.newScope(name, this.currentScope || undefined);
|
let scope = this.em.newScope(name, this.currentScope || undefined);
|
||||||
scope.filePath = this.path;
|
scope.filePath = this.path;
|
||||||
@ -382,7 +384,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseEntity() : Entity {
|
parseEntity(): Entity {
|
||||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||||
const scope = this.currentScope;
|
const scope = this.currentScope;
|
||||||
let entname = '';
|
let entname = '';
|
||||||
@ -392,53 +394,62 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
let etype = this.parseEntityArchetype();
|
let etype = this.parseEntityArchetype();
|
||||||
let entity = this.currentScope.newEntity(etype);
|
let entity = this.currentScope.newEntity(etype);
|
||||||
entity.name = entname;
|
entity.name = entname;
|
||||||
let cmd2 : string;
|
let cmd2: string;
|
||||||
// TODO: remove init?
|
// TODO: remove init?
|
||||||
while ((cmd2 = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') {
|
while ((cmd2 = this.expectTokens(['const', 'init', 'var', 'decode', 'end']).str) != 'end') {
|
||||||
let cmd = cmd2; // put in scope
|
let cmd = cmd2; // put in scope
|
||||||
if (cmd == 'var') cmd = 'init'; // TODO: remove?
|
if (cmd == 'var') cmd = 'init'; // TODO: remove?
|
||||||
if (cmd == 'init' || cmd == 'const') {
|
if (cmd == 'init' || cmd == 'const') {
|
||||||
// TODO: check data types
|
this.parseInitConst(cmd, scope, entity);
|
||||||
let name = this.expectIdent().str;
|
|
||||||
let { c, f } = this.getEntityField(entity, name);
|
|
||||||
let symtype = this.currentScope.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(() => {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (cmd == 'const') scope.setConstValue(entity, c, name, valueOrRef as DataValue);
|
|
||||||
if (cmd == 'init') scope.setInitValue(entity, c, name, valueOrRef as DataValue);
|
|
||||||
}
|
|
||||||
} else if (cmd == 'decode') {
|
} else if (cmd == 'decode') {
|
||||||
let decoderid = this.expectIdent().str;
|
this.parseDecode(scope, entity);
|
||||||
let codetok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
|
||||||
let code = codetok.str;
|
|
||||||
code = code.substring(3, code.length-3);
|
|
||||||
let decoder = newDecoder(decoderid, code);
|
|
||||||
if (!decoder) { this.compileError(`I can't find a "${decoderid}" decoder.`); throw new Error() }
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
result = decoder.parse();
|
|
||||||
} catch (e) {
|
|
||||||
throw new ECSError(e.message, decoder.getErrorLocation(codetok.$loc));
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEntityField(e: Entity, name: string) : ComponentFieldPair {
|
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 = 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, f, refvalue);
|
||||||
|
if (cmd == 'init') scope.setInitValue(entity, c, f, refvalue);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (cmd == 'const') scope.setConstValue(entity, c, f, valueOrRef as DataValue);
|
||||||
|
if (cmd == 'init') scope.setInitValue(entity, c, f, valueOrRef as DataValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseDecode(scope: EntityScope, entity: Entity) {
|
||||||
|
let decoderid = this.expectIdent().str;
|
||||||
|
let codetok = this.expectTokenTypes([ECSTokenType.CodeFragment]);
|
||||||
|
let code = codetok.str;
|
||||||
|
code = code.substring(3, code.length - 3);
|
||||||
|
let decoder = newDecoder(decoderid, code);
|
||||||
|
if (!decoder) { this.compileError(`I can't find a "${decoderid}" decoder.`); throw new Error() }
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = decoder.parse();
|
||||||
|
} catch (e) {
|
||||||
|
throw new ECSError(e.message, decoder.getErrorLocation(codetok.$loc));
|
||||||
|
}
|
||||||
|
for (let entry of Object.entries(result.properties)) {
|
||||||
|
let { c, f } = this.getEntityField(entity, entry[0]);
|
||||||
|
scope.setConstValue(entity, c, f, entry[1] as DataValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getEntityField(e: Entity, name: string): ComponentFieldPair {
|
||||||
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
if (!this.currentScope) { this.internalError(); throw new Error(); }
|
||||||
let comps = this.em.componentsWithFieldName([e.etype], name);
|
let comps = this.em.componentsWithFieldName([e.etype], name);
|
||||||
if (comps.length == 0) this.compileError(`I couldn't find a field named "${name}" for this entity.`)
|
if (comps.length == 0) this.compileError(`I couldn't find a field named "${name}" for this entity.`)
|
||||||
@ -449,14 +460,14 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
return { c: component, f: field };
|
return { c: component, f: field };
|
||||||
}
|
}
|
||||||
|
|
||||||
parseEntityArchetype() : EntityArchetype {
|
parseEntityArchetype(): EntityArchetype {
|
||||||
this.expectToken('[');
|
this.expectToken('[');
|
||||||
let components = this.parseList(this.parseComponentRef, ',');
|
let components = this.parseList(this.parseComponentRef, ',');
|
||||||
this.expectToken(']');
|
this.expectToken(']');
|
||||||
return {components};
|
return { components };
|
||||||
}
|
}
|
||||||
|
|
||||||
parseComponentRef() : ComponentType {
|
parseComponentRef(): ComponentType {
|
||||||
let name = this.expectIdent().str;
|
let name = this.expectIdent().str;
|
||||||
let cref = this.em.getComponentByName(name);
|
let cref = this.em.getComponentByName(name);
|
||||||
if (!cref) this.compileError(`I couldn't find a component named "${name}".`)
|
if (!cref) this.compileError(`I couldn't find a component named "${name}".`)
|
||||||
@ -473,7 +484,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
return eref;
|
return eref;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveEntityRef(scope: EntityScope, ref: ForwardRef) : number {
|
resolveEntityRef(scope: EntityScope, ref: ForwardRef): number {
|
||||||
let id = this.findEntityByName(scope, ref.token).id;
|
let id = this.findEntityByName(scope, ref.token).id;
|
||||||
if (ref.reftype) {
|
if (ref.reftype) {
|
||||||
// TODO: make this a function? elo ehi etc?
|
// TODO: make this a function? elo ehi etc?
|
||||||
@ -486,7 +497,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseSystemInstanceRef() : SystemInstance {
|
parseSystemInstanceRef(): SystemInstance {
|
||||||
let name = this.expectIdent().str;
|
let name = this.expectIdent().str;
|
||||||
let system = this.em.getSystemByName(name);
|
let system = this.em.getSystemByName(name);
|
||||||
if (!system) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc);
|
if (!system) this.compileError(`I couldn't find a system named "${name}".`, this.lasttoken.$loc);
|
||||||
@ -495,7 +506,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseSystemInstanceParameters() : SystemInstanceParameters {
|
parseSystemInstanceParameters(): SystemInstanceParameters {
|
||||||
let scope = this.currentScope;
|
let scope = this.currentScope;
|
||||||
if (scope == null) throw new Error();
|
if (scope == null) throw new Error();
|
||||||
if (this.peekToken().str == '[') {
|
if (this.peekToken().str == '[') {
|
||||||
@ -526,8 +537,7 @@ export class ECSCompiler extends Tokenizer {
|
|||||||
|
|
||||||
export class ECSActionCompiler extends Tokenizer {
|
export class ECSActionCompiler extends Tokenizer {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly context: ActionContext)
|
public readonly context: ActionContext) {
|
||||||
{
|
|
||||||
super();
|
super();
|
||||||
this.setTokenRules([
|
this.setTokenRules([
|
||||||
{ type: ECSTokenType.Placeholder, regex: /\{\{.*?\}\}/ },
|
{ type: ECSTokenType.Placeholder, regex: /\{\{.*?\}\}/ },
|
||||||
@ -542,7 +552,7 @@ export class ECSActionCompiler extends Tokenizer {
|
|||||||
while (!this.isEOF()) {
|
while (!this.isEOF()) {
|
||||||
let tok = this.consumeToken();
|
let tok = this.consumeToken();
|
||||||
if (tok.type == ECSTokenType.Placeholder) {
|
if (tok.type == ECSTokenType.Placeholder) {
|
||||||
let args = tok.str.substring(2, tok.str.length-2).split(/\s+/);
|
let args = tok.str.substring(2, tok.str.length - 2).split(/\s+/);
|
||||||
nodes.push(new CodePlaceholderNode(this.context, tok.$loc, args));
|
nodes.push(new CodePlaceholderNode(this.context, tok.$loc, args));
|
||||||
} else if (tok.type == TokenType.CatchAll) {
|
} else if (tok.type == TokenType.CatchAll) {
|
||||||
nodes.push(new CodeLiteralNode(this.context, tok.$loc, tok.str));
|
nodes.push(new CodeLiteralNode(this.context, tok.$loc, tok.str));
|
||||||
|
@ -309,9 +309,12 @@ export class Dialect_CA65 {
|
|||||||
return `.scope ${name}`
|
return `.scope ${name}`
|
||||||
}
|
}
|
||||||
endScope(name: string) {
|
endScope(name: string) {
|
||||||
return `.endscope\n${name}__Start = ${name}::__Start`
|
return `.endscope\n${this.scopeSymbol(name)} = ${name}::__Start`
|
||||||
// TODO: scope__start = scope::start
|
// TODO: scope__start = scope::start
|
||||||
}
|
}
|
||||||
|
scopeSymbol(name: string) {
|
||||||
|
return `${name}__Start`;
|
||||||
|
}
|
||||||
align(value: number) {
|
align(value: number) {
|
||||||
return `.align ${value}`;
|
return `.align ${value}`;
|
||||||
}
|
}
|
||||||
@ -365,6 +368,9 @@ export class Dialect_CA65 {
|
|||||||
call(symbol: string) {
|
call(symbol: string) {
|
||||||
return ` jsr ${symbol}`;
|
return ` jsr ${symbol}`;
|
||||||
}
|
}
|
||||||
|
jump(symbol: string) {
|
||||||
|
return ` jmp ${symbol}`;
|
||||||
|
}
|
||||||
return() {
|
return() {
|
||||||
return ' rts';
|
return ' rts';
|
||||||
}
|
}
|
||||||
@ -662,6 +668,8 @@ class ActionEval {
|
|||||||
else state.xreg = new IndexRegister(this.scope, this.qr);
|
else state.xreg = new IndexRegister(this.scope, this.qr);
|
||||||
break;
|
break;
|
||||||
case 'join':
|
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);
|
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);
|
this.jr = new EntitySet(this.scope, (this.action as ActionWithJoin).join);
|
||||||
state.xreg = new IndexRegister(this.scope, this.jr);
|
state.xreg = new IndexRegister(this.scope, this.jr);
|
||||||
@ -882,6 +890,10 @@ class ActionEval {
|
|||||||
//this.used.add(`arg_${argindex}_${argvalue}`);
|
//this.used.add(`arg_${argindex}_${argvalue}`);
|
||||||
return 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 {
|
wrapCodeInLoop(code: string, action: ActionWithQuery, ents: Entity[], joinfield?: ComponentFieldPair): string {
|
||||||
// TODO: check ents
|
// TODO: check ents
|
||||||
// TODO: check segment bounds
|
// TODO: check segment bounds
|
||||||
@ -976,9 +988,8 @@ class ActionEval {
|
|||||||
if (baseLookup) {
|
if (baseLookup) {
|
||||||
return this.dialect.absolute(ident);
|
return this.dialect.absolute(ident);
|
||||||
} else if (entities.length == 1) {
|
} else if (entities.length == 1) {
|
||||||
let eidofs = qr.entities.length && qr.entities[0].id - range.elo; // TODO: negative?
|
// TODO: qr or this.entites?
|
||||||
if (entityLookup)
|
let eidofs = entities[0].id - range.elo; // TODO: negative?
|
||||||
eidofs = entities[0].id - range.elo;
|
|
||||||
return this.dialect.absolute(ident, eidofs);
|
return this.dialect.absolute(ident, eidofs);
|
||||||
} else {
|
} else {
|
||||||
let ir;
|
let ir;
|
||||||
@ -1298,15 +1309,17 @@ export class EntityScope implements SourceLocated {
|
|||||||
return this.bss.getFieldRange(c, fn) || this.rodata.getFieldRange(c, fn);
|
return this.bss.getFieldRange(c, fn) || this.rodata.getFieldRange(c, fn);
|
||||||
}
|
}
|
||||||
// TODO: check type/range of value
|
// TODO: check type/range of value
|
||||||
setConstValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) {
|
setConstValue(e: Entity, component: ComponentType, field: DataField, value: DataValue) {
|
||||||
this.setConstInitValue(e, component, fieldName, value, 'const');
|
this.setConstInitValue(e, component, field, value, 'const');
|
||||||
}
|
}
|
||||||
setInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue) {
|
setInitValue(e: Entity, component: ComponentType, field: DataField, value: DataValue) {
|
||||||
this.setConstInitValue(e, component, fieldName, value, 'init');
|
this.setConstInitValue(e, component, field, value, 'init');
|
||||||
}
|
}
|
||||||
setConstInitValue(e: Entity, component: ComponentType, fieldName: string, value: DataValue,
|
setConstInitValue(e: Entity, component: ComponentType, field: DataField, value: DataValue,
|
||||||
type: 'const'|'init') {
|
type: 'const'|'init')
|
||||||
this.em.singleComponentWithFieldName([e.etype], fieldName, e);
|
{
|
||||||
|
this.checkValueType(field, value);
|
||||||
|
let fieldName = field.name;
|
||||||
let cfname = mksymbol(component, fieldName);
|
let cfname = mksymbol(component, fieldName);
|
||||||
let ecfname = mkscopesymbol(this, component, fieldName);
|
let ecfname = mkscopesymbol(this, component, fieldName);
|
||||||
if (e.consts[cfname] !== undefined) throw new ECSError(`"${fieldName}" is already defined as a constant`, e);
|
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' {
|
isConstOrInit(component: ComponentType, fieldName: string) : 'const' | 'init' {
|
||||||
return this.fieldtypes[mksymbol(component, fieldName)];
|
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 {
|
generateCodeForEvent(event: string, args?: string[], codelabel?: string): string {
|
||||||
// find systems that respond to event
|
// find systems that respond to event
|
||||||
// and have entities in this scope
|
// and have entities in this scope
|
||||||
|
@ -159,6 +159,7 @@ end
|
|||||||
entity NullShape [Bitmap,Colormap]
|
entity NullShape [Bitmap,Colormap]
|
||||||
decode vcs_sprite
|
decode vcs_sprite
|
||||||
---
|
---
|
||||||
|
........ 00
|
||||||
---
|
---
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -504,7 +505,7 @@ x.x.x.x.x.x.x.x.x.x. .. 06 ..
|
|||||||
var sprite = #Superdude
|
var sprite = #Superdude
|
||||||
end
|
end
|
||||||
entity Slot1 [SpriteSlot]
|
entity Slot1 [SpriteSlot]
|
||||||
var sprite = $ff
|
var sprite = 0 // $ff
|
||||||
end
|
end
|
||||||
|
|
||||||
using VersatilePlayfield with #Superdude.room
|
using VersatilePlayfield with #Superdude.room
|
||||||
|
@ -60,8 +60,9 @@ Bitmap_bitmapdata_b8:
|
|||||||
.byte >(Bitmap_bitmapdata_e2_b0+31)
|
.byte >(Bitmap_bitmapdata_e2_b0+31)
|
||||||
.byte >(Bitmap_bitmapdata_e3_b0+31)
|
.byte >(Bitmap_bitmapdata_e3_b0+31)
|
||||||
Bitmap_bitmapdata_e1_b0:
|
Bitmap_bitmapdata_e1_b0:
|
||||||
|
.byte 0
|
||||||
Bitmap_height_b0:
|
Bitmap_height_b0:
|
||||||
.byte 255
|
.byte 0
|
||||||
.byte 17
|
.byte 17
|
||||||
.byte 27
|
.byte 27
|
||||||
Colormap_colormapdata_b0:
|
Colormap_colormapdata_b0:
|
||||||
@ -73,6 +74,7 @@ Colormap_colormapdata_b8:
|
|||||||
.byte >(Colormap_colormapdata_e2_b0+31)
|
.byte >(Colormap_colormapdata_e2_b0+31)
|
||||||
.byte >(Colormap_colormapdata_e3_b0+31)
|
.byte >(Colormap_colormapdata_e3_b0+31)
|
||||||
Colormap_colormapdata_e1_b0:
|
Colormap_colormapdata_e1_b0:
|
||||||
|
.byte 0
|
||||||
Bitmap_bitmapdata_e2_b0:
|
Bitmap_bitmapdata_e2_b0:
|
||||||
.byte 128
|
.byte 128
|
||||||
.byte 192
|
.byte 192
|
||||||
@ -622,7 +624,7 @@ Main__INITDATA:
|
|||||||
.byte 60
|
.byte 60
|
||||||
.byte 90
|
.byte 90
|
||||||
.byte 0
|
.byte 0
|
||||||
.byte 255
|
.byte 0
|
||||||
.byte 0
|
.byte 0
|
||||||
.byte 0
|
.byte 0
|
||||||
.byte 0
|
.byte 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user