Skip to content

Commit

Permalink
AST transformers: fix issues, increase type safety
Browse files Browse the repository at this point in the history
  • Loading branch information
alessiostalla committed Dec 6, 2023
1 parent 34a9ab5 commit df4b3ca
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 25 deletions.
42 changes: 30 additions & 12 deletions src/transformation/transformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ensureNodeDefinition,
getNodeDefinition,
Node,
NODE_DEFINITION_SYMBOL,
NODE_DEFINITION_SYMBOL, NodeDefinition,
Origin,
registerNodeDefinition,
registerNodeProperty
Expand All @@ -18,15 +18,32 @@ export class PropertyRef<Obj, Value> {
public readonly get: (o: Obj) => Value | undefined,
public readonly set: (o: Obj, v: Value) => void) {}

static get<Obj, Value>(name: string | symbol | number): PropertyRef<Obj, Value> {
static get<Obj, Value>(name: string | symbol | number, nodeDefinition?: NodeDefinition): PropertyRef<Obj, Value> {
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<Obj, Value>(
name,
obj => obj[name],
getter,
(obj, value) => obj[name] = value
);
}
Expand Down Expand Up @@ -73,13 +90,10 @@ export class NodeFactory<Source, Output extends Node> {
* the parent has been instantiated.
*/
withChild<Target, Child>(child: ChildDef<Source, Target, Child>) : NodeFactory<Source, Output> {
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") {
Expand All @@ -95,7 +109,7 @@ export class NodeFactory<Source, Output extends Node> {
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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -458,6 +473,9 @@ export function Init(target, methodName: string): void {
// Transformations //
//-----------------//

/**
* @deprecated please use StarLasu AST transformers.
*/
export function fillChildAST<FROM, TO extends Node>(
node: TO, property: string, tree: FROM | undefined, transformer: (node: FROM) => TO | undefined): TO[] {
const propDef: any = ensureNodeDefinition(node).properties[property];
Expand Down
14 changes: 2 additions & 12 deletions tests/mapping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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);
});
});

Expand Down
3 changes: 2 additions & 1 deletion tests/transformation/transformation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {

@ASTNode("", "A")
class A extends Node {
@Child()
child: Node;
property: number;
other: string;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit df4b3ca

Please sign in to comment.