Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/samvv/tsastgen
Generate TypeScript AST definitions from a simple specification file
https://github.com/samvv/tsastgen
Last synced: about 1 month ago
JSON representation
Generate TypeScript AST definitions from a simple specification file
- Host: GitHub
- URL: https://github.com/samvv/tsastgen
- Owner: samvv
- License: mit
- Created: 2020-05-30T14:10:15.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2023-02-09T14:12:04.000Z (almost 2 years ago)
- Last Synced: 2024-11-10T09:34:47.200Z (about 2 months ago)
- Language: TypeScript
- Homepage: https://npmjs.com/package/tsastgen
- Size: 275 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
AST Generator for TypeScript
============================This tool generates complete definitions for an abstract syntax tree (AST) in
the TypeScript language. It reads an ordinary TypeScript file as a
specification file, and will automatically generate classes, contstructors,
union types, visitors, and predicates based on this specification.## Features
- Mix your own code with code generated by `tsastgen`. TypeScript goes in,
TypeScript comes out.
- Efficient type-checking and dispatching using TypeScript enums and
discriminated unions.
- Generate typed reflection functions such as `Node.getChildNodes()` and
`isFoo()`
- Generate typed factory functions that are able to automatically lift simple
primitive values to entire nodes _(experimental)_.
- Ability to generate typed links to the parent node that only lists node
types that can actually be a parent.## Basic Usage
First you have to install the package:
```
npm install -g tsastgen
````Now a binary called `tsastgen` should be available in your favorite shell.
If not, check your `PATH` variable and that _npm_ is properly configured.Next, create a specification file. Here's an example:
**calc-spec.ts**
```ts
export interface AST {}export interface Definition extends AST {
name: string;
expression: Expression;
}export interface Expression extends AST {
lazyEvaluatedResult?: number;
}export interface ConstantExpression extends Expression {
value: number;
}export interface BinaryExpression extends Expression {
left: Expression;
right: Expression;
}export interface SubtractExpression extends BinaryExpression {
}
export interface AddExpression extends BinaryExpression {
}
export interface MultiplyExpression extends BinaryExpression {
}
export interface DivideExpression extends BinaryExpression {
}
export type CommutativeExpression
= AddExpression
| SubtractExpression
| MultiplyExpression;
```Now all you need to do is to run `tsastgen` and make sure it knows what the
output file and the root node is.```
tsastgen --root-node=AST calc-spec.ts:calc.ts
```Read the [example][1] for a much more detailed explanation of how code is generated.
[1]: https://github.com/samvv/tsastgen/blob/master/examples/calculator.ts
### How to match certain generated AST nodes
Here's an example of how the generated code might be used:
```ts
import { Expression } from "./calc"export function calculate(node: Expression): number {
switch (node.kind) {
case SyntaxKind.AddExpression:
return node.left + node.right;
case SyntaxKind.SubtractExpression:
// and so on ...
default:
throw new Error(`I did not know how to process the given node.`);
}
}
```In the above example, due to the way in which the code is generated, the
compiler automatically knows when certain fields are present.Alternatively, you can use the generated AST predicates combined with an
if-statement to prevent casting:```ts
const node = generateANewNodeSomehow();if (isDefinition(node)) {
// The fields 'name' and 'expression' are now available.
}
```No matter which style you use, you will almost never have to cast to another
expression.### How to create new AST nodes
Creating nodes is also very easy:
```ts
import {
createAddExpression,
createConstantExpression,
} from "./calc";const n1 = createConstantExpression(1);
const n2 = createConstantExpression(2);
const add = createAddExpression(n1, n2);console.log(`The result of 1 + 2 is ${calculate(add)}`);
```It is recommended to not use the `new` operator. Instead, use the wrapping
`createX` function. The motivation is that in the future we might use a more
efficient representation than a class, using `createX`-functions guarantees
forward compatibility.## API
In the following documented signatures, `Foo` stands for an arbitrary node type
generated by `tsastgen` and `node` for an instance of `Foo`.
`Syntax` refers to the node type specified with `--root-node` and `field` to a
specified field of `Foo`.### SyntaxKind
A large enumeration that contains all of your node types. You can access the
kind of a node using the `node.kind` property documented below.### isSyntax(value)
Check whether `value` is an AST node. Any JavaScript value may be passed in.
Use this function if you don't know anything about `value` and before using a
function such as `isFoo`.### isFoo(node)
Check whether the given node is of the specific node type `Foo`. For
performance reasons, you should only pass in valid node objects. Do not use
this function to check whether any random JavaScript value is a node
object.### node.kind
Get the `SyntaxKind` enumeration value that discriminates the type of this
node from the other node types.### node.getChildNodes()
Returns an iterable (not an array) of all nodes that can be found in the fields
of `node`.### node.parentNode
A typed reference to a node that is the parent of this node, or `null` if this
node is the root node or if the parent has not been set.### node.field
A property to get the value of a specified field of `node`.
### FooParent
A type alias that lists all node types that in theory can be a parent of `Foo`.
### FooChild
A type alias that lists all node types that in theory can occur in the fields
of `Foo`.### createFoo(...fields)
Create a new node object of the type `Foo`. The required fields go first after
which the optional fields may be specified. Use your code editor's hint dialogs
to see what field goes where.If a field refers to a node that only contains one field, you may be able to
just specify that field directly and `createFoo` will convert it into the
desired node type for you.## CLI Options
```
tsastgen [input-file[:output-file]..] --root-node=
```### input-file
The specification file that AST definitions will be generated from.
### output-file
If present, the file where the transformed `input-file` must be written to.
If not present, the program will output the result to standard output.
### --root-node
The name of the node that serves as the root node of the abstract syntax
tree.It will automatically be converted to a union type containing all possible
AST node types.If `--root-node` is not specified, _tsastgen_ will search for a declaration
named `Syntax`.### --with-parent-member
The name of the field that is used to refer to the parent node of a node type.
If enabled, `tsastgen` will treat this node specially and inject/replace fully
typed member fields with the name you provided.If `--with-parent-member` is not specified, this feature is not enabled and
your member will be passed through without any modifications.### --with-mutators
Also generate methods that mutate a node or field on each node type.
### --with-coercions
Enable experimental code generation of coercion statements in factory
functions. Currently, due to limitations in the built-in type checking
algorithm, this will not work for complex ASTs.## Known Issues and Limitations
Coercions in factory functions will not always be generated because we only
emulate a small subset the typing rule of TypeScript. If something cannot be
checked, the tool will error or skip it. This is due to poor support of the
TypeScript compiler to inspect the types of AST nodes in detail. Unfortunately,
this appears to be by design, so this won't get fixed easily. If we were to
cover all cases of TypeScript's AST, we would effectively have written a second
compiler.## License
I chose to license this piece of software under the MIT license, in the hope
that you may find it useful. You may freely use this generator in your own
projects, but it is always nice if you can give a bit of credit.