@jfhbrook/citree
TypeScript icon, indicating that this package has built-in type declarations

1.0.2 • Public • Published

citree

citree is a DSL for generating TypeScript AST files. It's inspired by the Java AST generator in Crafting Interpreters by Robert Nystrom.

Install

You can install citree into your project with npm:

npm i --save-dev @jfhbrook/citree

Usage

The best way to see how citree works is to look at the example:

Creating a Specification

First, make an ast.citree file. That would look something like this:

type Expr in './expr' {
  import { TokenKind } from '../tokens'

  Unary          => op: TokenKind, expr: Expr
  Binary         => left: Expr, op: TokenKind, right: Expr
  Logical        => left: Expr, op: TokenKind, right: Expr
  Group          => expr: Expr
  IntLiteral     => value: number
  RealLiteral    => value: number
  BoolLiteral    => value: boolean
  StringLiteral  => value: string
  PromptLiteral  => value: string
  NilLiteral!
}

type Cmd in './cmd' {
  import { Expr } from './expr'

  Cmd => offsetStart: number = -1, offsetEnd: number = -1
  Expression => expression: Expr
  Print      => expression: Expr
  Exit       => expression: Expr | null
}

type Tree in './index' {
  import { Cmd } from './cmd'

  CommandGroup => row: number, source: string, commands: Cmd[]
  Line         => lineNo: number, row: number, source: string, commands: Cmd[]
  Input        => input: Array<CommandGroup | Line>
  Program      => lines: Line[]
}

Running citree

Then, run citree against that file:

npx @jfhbrook/citree -- ast.citree

This will generate two files, expr.ts and stmt.ts. Each of these files will contain a class, a visitor interface, and a series of subclasses corresponding to the specified types. It will also contain classes corresponding to each type.

For example, expr.ts will look like this:

import { TokenKind } from '../tokens';

export interface ExprVisitor<R> {
  visitUnaryExpr(node: Unary): R;
  visitBinaryExpr(node: Binary): R;
  visitLogicalExpr(node: Logical): R;
  visitGroupExpr(node: Group): R;
  visitIntLiteralExpr(node: IntLiteral): R;
  visitRealLiteralExpr(node: RealLiteral): R;
  visitBoolLiteralExpr(node: BoolLiteral): R;
  visitStringLiteralExpr(node: StringLiteral): R;
  visitPromptLiteralExpr(node: PromptLiteral): R;
  visitNilLiteralExpr(node: NilLiteral): R;
}

export abstract class Expr {
  abstract accept<R>(visitor: ExprVisitor<R>): R;
}

export class Unary extends Expr {
  constructor(
    public op: TokenKind,
    public expr: Expr,
  ) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitUnaryExpr(this);
  }
}

export class Binary extends Expr {
  constructor(
    public left: Expr,
    public op: TokenKind,
    public right: Expr,
  ) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitBinaryExpr(this);
  }
}

export class Logical extends Expr {
  constructor(
    public left: Expr,
    public op: TokenKind,
    public right: Expr,
  ) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitLogicalExpr(this);
  }
}

export class Group extends Expr {
  constructor(public expr: Expr) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitGroupExpr(this);
  }
}

export class IntLiteral extends Expr {
  constructor(public value: number) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitIntLiteralExpr(this);
  }
}

export class RealLiteral extends Expr {
  constructor(public value: number) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitRealLiteralExpr(this);
  }
}

export class BoolLiteral extends Expr {
  constructor(public value: boolean) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitBoolLiteralExpr(this);
  }
}

export class StringLiteral extends Expr {
  constructor(public value: string) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitStringLiteralExpr(this);
  }
}

export class PromptLiteral extends Expr {
  constructor(public value: string) {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitPromptLiteralExpr(this);
  }
}

export class NilLiteral extends Expr {
  constructor() {
    super();
  }

  accept<R>(visitor: ExprVisitor<R>): R {
    return visitor.visitNilLiteralExpr(this);
  }
}

Using the Generated Files

For a complete example of using the generated files, check out Matanuska BASIC, which uses citree to generate its AST.

Testing

You can run current tests with npm:

npm test

This includes an end-to-end test with the example.

License

This project is MIT licensed. For more, see ./LICENSE.

Readme

Keywords

Package Sidebar

Install

npm i @jfhbrook/citree

Weekly Downloads

3

Version

1.0.2

License

MIT

Unpacked Size

183 kB

Total Files

77

Last publish

Collaborators

  • jfhbrook