From 87eb8e2f05a66bf0ad445b5d4a09cdd45997ea51 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Jan 2022 09:17:40 -0600 Subject: [PATCH] ecs: event2system --- src/codemirror/ecs.js | 4 ++++ src/common/ecs/compiler.ts | 8 ++++--- src/common/ecs/ecs.ts | 46 +++++++++++++------------------------- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/codemirror/ecs.js b/src/codemirror/ecs.js index 947b0806..e1b180c2 100644 --- a/src/codemirror/ecs.js +++ b/src/codemirror/ecs.js @@ -37,6 +37,7 @@ var opcodes = /^[a-z][a-z][a-z]\b/i; var numbers = /^(0x[\da-f]+|[\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; var tags = /^\{\{.*\}\}/; + var comment = /\/\/.*/; var mlcomment = /^---.+?---\b/i; return { @@ -52,6 +53,9 @@ if (stream.match(tags)) { return 'meta'; } + if (stream.match(comment)) { + return 'comment'; + } var w; if (stream.eatWhile(/\w/)) { diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 904e4348..e738cd7f 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -1,5 +1,5 @@ -import { Tokenizer, TokenType } from "../tokenizer"; +import { mergeLocs, Tokenizer, TokenType } from "../tokenizer"; import { SourceLocated } from "../workertypes"; import { Action, ArrayType, ComponentType, DataField, DataType, DataValue, Dialect_CA65, Entity, EntityArchetype, EntityManager, EntityScope, IntType, Query, RefType, SelectType, SourceFileExport, System } from "./ecs"; @@ -27,6 +27,7 @@ export class ECSCompiler extends Tokenizer { { type: ECSTokenType.Integer, regex: /[-]?\$[A-Fa-f0-9]+/ }, { type: ECSTokenType.Integer, regex: /[-]?\d+/ }, { type: TokenType.Ident, regex: /[A-Za-z_][A-Za-z0-9_]*/ }, + { type: TokenType.Ignore, regex: /\/\/.*/ }, { type: TokenType.Ignore, regex: /\s+/ }, ]); this.errorOnCatchAll = true; @@ -185,10 +186,11 @@ export class ECSCompiler extends Tokenizer { parseQuery() { let q: Query = { include: [] }; - this.expectToken('['); + let start = this.expectToken('['); q.include = this.parseList(this.parseComponentRef, ',').map(c => c.name); // TODO: other params - this.expectToken(']'); + let end = this.expectToken(']'); + q.$loc = mergeLocs(start.$loc, end.$loc); return q; } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index 78c90de5..73781b5c 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -77,7 +77,7 @@ export interface ComponentType extends SourceLocated { optional?: boolean; } -export interface Query { +export interface Query extends SourceLocated { include: string[]; // TODO: make ComponentType listen?: string[]; exclude?: string[]; @@ -407,37 +407,14 @@ export class EntityScope implements SourceLocated { } return result; } - getSystems(events: string[]) { - let result : System[] = []; - for (let sys of Object.values(this.em.systems)) { - if (this.systemListensTo(sys, events)) { - result.push(sys); - } - } - return result; - } - systemListensTo(sys: System, events: string[]) { - for (let action of sys.actions) { - if (action.event != null && events.includes(action.event)) { - let archs = this.em.archetypesMatching(action.query); - for (let arch of archs) { - for (let ctype of arch.cmatch) { - if (this.hasComponent(ctype)) { - return true; - } - } - } - } - } - } hasComponent(ctype: ComponentType) { return this.componentsInScope.has(ctype.name); } getJoinField(action: Action, atypes: ArchetypeMatch[], jtypes: ArchetypeMatch[]) : ComponentFieldPair { let refs = Array.from(this.iterateArchetypeFields(atypes, (c,f) => f.dtype == 'ref')); // TODO: better error message - if (refs.length == 0) throw new ECSError(`cannot find join fields`, action); - if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action); + if (refs.length == 0) throw new ECSError(`cannot find join fields`, action.query); + if (refs.length > 1) throw new ECSError(`cannot join multiple fields`, action.query); // TODO: check to make sure join works return refs[0]; // TODO /* TODO @@ -572,8 +549,8 @@ export class EntityScope implements SourceLocated { generateCodeForEvent(event: string): string { // find systems that respond to event // and have entities in this scope - let systems = this.getSystems([event]); - if (systems.length == 0) { + let systems = this.em.event2system[event]; + if (!systems || systems.length == 0) { console.log(`; warning: no system responds to ${event}`); // TODO: warning } let s = ''; @@ -620,7 +597,6 @@ export class EntityScope implements SourceLocated { let label = `${sys.name}__${action.event}`; let atypes = this.em.archetypesMatching(action.query); let entities = this.entitiesMatching(atypes); - if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`, action); // TODO // TODO: detect cycles // TODO: "source"? // TODO: what if only 1 item? @@ -631,6 +607,8 @@ export class EntityScope implements SourceLocated { if (action.select == 'join' && action.join) { let jtypes = this.em.archetypesMatching(action.join); let jentities = this.entitiesMatching(jtypes); + if (jentities.length == 0) + throw new ECSError(`join query for ${label} doesn't match any entities`, action.join); // TODO let joinfield = this.getJoinField(action, atypes, jtypes); // TODO: what if only 1 item? // TODO: should be able to access fields via Y reg @@ -643,7 +621,8 @@ export class EntityScope implements SourceLocated { if (action.limit) { entities = entities.slice(0, action.limit); } - if (entities.length == 0) throw new ECSError(`action ${label} doesn't match any entities`); // TODO + if (entities.length == 0) + throw new ECSError(`query for ${label} doesn't match any entities`, action.query); // TODO // define properties props['%elo'] = entities[0].id.toString(); props['%ehi'] = entities[entities.length - 1].id.toString(); @@ -786,6 +765,7 @@ export class EntityManager { systems: { [name: string]: System } = {}; scopes: { [name: string]: EntityScope } = {}; symbols: { [name: string] : 'init' | 'const' } = {}; + event2system: { [name: string]: System[] } = {}; constructor(public readonly dialect: Dialect_CA65) { } @@ -801,6 +781,12 @@ export class EntityManager { } defineSystem(system: System) { if (this.systems[system.name]) throw new ECSError(`system ${system.name} already defined`); + for (let a of system.actions) { + let event = a.event; + let list = this.event2system[event]; + if (list == null) list = this.event2system[event] = []; + if (!list.includes(system)) list.push(system); + } return this.systems[system.name] = system; } addArchetype(atype: EntityArchetype) : EntityArchetype {