diff --git a/src/transformation/transformation.ts b/src/transformation/transformation.ts index 13144b3..7d8f804 100644 --- a/src/transformation/transformation.ts +++ b/src/transformation/transformation.ts @@ -3,7 +3,7 @@ import { ensureNodeDefinition, getNodeDefinition, Node, - NODE_DEFINITION_SYMBOL, + NODE_DEFINITION_SYMBOL, NodeDefinition, Origin, registerNodeDefinition, registerNodeProperty @@ -18,15 +18,32 @@ export class PropertyRef { public readonly get: (o: Obj) => Value | undefined, public readonly set: (o: Obj, v: Value) => void) {} - static get(name: string | symbol | number): PropertyRef { + static get(name: string | symbol | number, nodeDefinition?: NodeDefinition): PropertyRef { if (typeof name == "symbol") { name = name.toString(); } else if (typeof name == "number") { name = name + ""; } + + if (nodeDefinition) { + const property = Object.keys(nodeDefinition.properties).find(p => p == name); + if (!property) { + throw new Error(`${name} is not a feature of ${nodeDefinition}`) + } + } + + function getter(obj: Obj): Value { + const value = obj[name]; + if (typeof value === "function") { // ANTLR defines accessor functions in some cases + return value.call(obj); + } else { + return value; + } + } + return new PropertyRef( name, - obj => obj[name], + getter, (obj, value) => obj[name] = value ); } @@ -73,13 +90,10 @@ export class NodeFactory { * the parent has been instantiated. */ withChild(child: ChildDef) : NodeFactory { - let prefix = ""; - if (child.scopedToType) { - const nodeDefinition = getNodeDefinition(child.scopedToType); - prefix = nodeDefinition?.name ? nodeDefinition.name : (child.scopedToType?.name || ""); - if (prefix) { - prefix += "#"; - } + const nodeDefinition = child.scopedToType ? getNodeDefinition(child.scopedToType) : undefined; + let prefix = nodeDefinition?.name ? nodeDefinition.name : (child.scopedToType?.name || ""); + if (prefix) { + prefix += "#"; } let source = child.source; if (typeof source == "string" || typeof source == "symbol" || typeof source == "number") { @@ -95,7 +109,7 @@ export class NodeFactory { this.childrenSetAtConstruction = true; } if (typeof target == "string" || typeof target == "symbol" || typeof target == "number") { - target = PropertyRef.get(target); + target = PropertyRef.get(target, nodeDefinition); } if (target instanceof PropertyRef) { if (name && name != target.name) { @@ -274,7 +288,8 @@ export class ASTTransformer { if (prefix) { prefix += "#"; } - Object.keys(node).forEach(propertyName => { + const properties = nodeDefinition ? Object.keys(nodeDefinition.properties) : Object.keys(node); + properties.forEach(propertyName => { const childNodeFactory = factory.getChildNodeFactory(prefix, propertyName); if (childNodeFactory) { if (childNodeFactory !== NO_CHILD_NODE) { @@ -458,6 +473,9 @@ export function Init(target, methodName: string): void { // Transformations // //-----------------// +/** + * @deprecated please use StarLasu AST transformers. + */ export function fillChildAST( node: TO, property: string, tree: FROM | undefined, transformer: (node: FROM) => TO | undefined): TO[] { const propDef: any = ensureNodeDefinition(node).properties[property]; diff --git a/tests/mapping.test.ts b/tests/mapping.test.ts index da4366b..4084046 100644 --- a/tests/mapping.test.ts +++ b/tests/mapping.test.ts @@ -13,14 +13,9 @@ class MySetStatement extends Node { //Explicit mapping @Child() id: Node; - //Implicit mapping (same name) - @Child() - EQUAL: Node; //No mapping (name doesn't match) @Child() set: Node; - @Child() - expression: any; //Erroneous mapping @Child() @Mapped("nonExistent") @@ -80,21 +75,16 @@ describe('Mapping of Parse Trees to ASTs', function() { source: "ID", target: (s: MySetStatement, id: Node) => s.id = id, name: "id", - scopedToType: SetStmtContext + scopedToType: MySetStatement }); const mySetStatement = transformer.transform(setStmt) as MySetStatement; expect(mySetStatement).to.be.instanceof(MySetStatement); expect(mySetStatement.origin instanceof ParseTreeOrigin).to.be.true; const origin = mySetStatement.origin as ParseTreeOrigin; expect(origin.parseTree).to.equal(setStmt); - expect(mySetStatement.id).not.to.be.undefined; - expect(mySetStatement.EQUAL).not.to.be.undefined; + expect(mySetStatement.id).to.be.instanceof(GenericNode); expect(mySetStatement.set).to.be.undefined; - expect(mySetStatement.expression).not.to.be.undefined; expect(mySetStatement.nonExistent).to.be.undefined; - - const expression = mySetStatement.expression; - expect(expression).to.be.instanceof(GenericNode); }); }); diff --git a/tests/transformation/transformation.test.ts b/tests/transformation/transformation.test.ts index bae3fca..5faa277 100644 --- a/tests/transformation/transformation.test.ts +++ b/tests/transformation/transformation.test.ts @@ -15,6 +15,7 @@ import { @ASTNode("", "A") class A extends Node { + @Child() child: Node; property: number; other: string; @@ -155,7 +156,7 @@ describe("Transformers", function () { const transformer = new ASTTransformer(undefined, true); transformer.registerIdentityTransformation(A) .withChild({ source: "child", target: "child" }); - transformer.registerNodeFactory(C,(source) => new A()); + transformer.registerNodeFactory(C, () => new A()); const transformedTree = transformer.transform(tree); expect(transformedTree).to.be.instanceof(A);