Skip to content

Commit

Permalink
Add AST, AST visitor, printer test
Browse files Browse the repository at this point in the history
  • Loading branch information
danvk committed Jan 9, 2024
1 parent dbcd077 commit 5e53ce1
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"lint:packages": "pnpm dedupe --check",
"lint:spelling": "cspell \"**\" \".github/**/*\"",
"prepare": "husky install",
"repl": "node --loader ts-node/esm --no-warnings src/index.ts",
"repl": "pnpm run:ts src/index.ts",
"run:ts": "node --loader ts-node/esm --no-warnings",
"should-semantic-release": "should-semantic-release --verbose",
"test": "vitest",
"tsc": "tsc"
Expand Down
41 changes: 41 additions & 0 deletions src/ast.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, test } from "vitest";

import { Expr, ExpressionVisitor, visitExpr } from "./ast.js";

const printer: ExpressionVisitor<string> = {
binary: (expr) => parenthesize(expr.operator.lexeme, expr.left, expr.right),
grouping: (expr) => parenthesize("group", expr.expr),
literal: (expr) => (expr.value === null ? "nil" : String(expr.value)),
unary: (expr) => parenthesize(expr.operator.lexeme, expr.right),
};

function parenthesize(name: string, ...exprs: Expr[]) {
const parts = ["(", name];
for (const expr of exprs) {
parts.push(" ");
parts.push(visitExpr(expr, printer));
}
parts.push(")");
return parts.join("");
}

test("AST Visitor", () => {
const expr: Expr = {
kind: "binary",
left: {
kind: "unary",
operator: { lexeme: "-", line: 1, literal: null, type: "-" },
right: { kind: "literal", value: 123 },
},
operator: { lexeme: "*", line: 1, literal: null, type: "*" },
right: {
expr: {
kind: "literal",
value: 45.67,
},
kind: "grouping",
},
};
const text = visitExpr(expr, printer);
expect(text).toEqual("(* (- 123) (group 45.67))");
});
34 changes: 34 additions & 0 deletions src/ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Token } from "./token.js";

export interface Binary {
kind: "binary";
left: Expr;
operator: Token;
right: Expr;
}

export interface Grouping {
expr: Expr;
kind: "grouping";
}

export interface Literal {
kind: "literal";
value: null | number | string;
}

export interface Unary {
kind: "unary";
operator: Token;
right: Expr;
}

export type Expr = Binary | Grouping | Literal | Unary;

export type ExpressionVisitor<R> = {
[Kind in Expr["kind"]]: (expr: Extract<Expr, { kind: Kind }>) => R;
};

export function visitExpr<R>(expr: Expr, visitor: ExpressionVisitor<R>): R {
return visitor[expr.kind](expr as never);
}
1 change: 1 addition & 0 deletions src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TokenType } from "./token-type.js";
export interface Token {
lexeme: string;
line: number;
// XXX: not all TokenTypes should have "literal"
literal: null | number | string;
type: TokenType;
}
Expand Down

0 comments on commit 5e53ce1

Please sign in to comment.