Move 'treegen' to seperate directory

This commit is contained in:
Sam Vervaeck 2020-05-30 11:29:08 +02:00
parent 4251c6eeaf
commit dd53ca87f3
14 changed files with 132 additions and 120 deletions

11
treegen/Makefile Normal file
View file

@ -0,0 +1,11 @@
all: lib/parser.js
lib/parser.js: src/parser.pegjs
@echo "Generating parser ..."
@mkdir -p lib/
@if ! pegjs --output lib/parser.js src/parser.pegjs; then \
rm -rf lib/parser.js; \
exit 1; \
fi

5
treegen/package-lock.json generated Normal file
View file

@ -0,0 +1,5 @@
{
"name": "treegen",
"version": "1.0.0",
"lockfileVersion": 1
}

29
treegen/package.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "treegen",
"version": "1.0.0",
"private": true,
"description": "An AST tree generator from a specification file.",
"main": "lib/index.js",
"bin": {
"treegen": "lib/cli.js"
},
"scripts": {
"prepare": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/samvv/BoltJS.git"
},
"keywords": [
"abstract-syntaxt-tree",
"specification-file",
"code-generator"
],
"author": "Sam Vervaeck",
"license": "MIT",
"bugs": {
"url": "https://github.com/samvv/BoltJS/issues"
},
"homepage": "https://github.com/samvv/BoltJS#readme"
}

0
treegen/snippets/ast-after.d.ts vendored Normal file
View file

View file

View file

@ -1,7 +1,5 @@
const exported = {};
class NodeVisitor {
export class NodeVisitor {
visit(node) {
for (const child of node.preorder()) {
const key = `visit${kindToString(child.kind)}`;
@ -12,9 +10,15 @@ class NodeVisitor {
}
}
exported.NodeVisitor = NodeVisitor;
let nextNodeId = 1;
const nodeProto = {
class SyntaxBase {
constructor(span) {
this.id = nextNodeId++;
this.errors = [];
this.span = span;
}
*getChildNodes() {
for (const key of Object.keys(this)) {
@ -34,7 +38,7 @@ const nodeProto = {
}
}
}
},
}
visit(visitors) {
const stack = [this];
@ -54,7 +58,7 @@ const nodeProto = {
stack.push(childNode);
}
}
},
}
*preorder() {
const stack = [this];
@ -65,12 +69,12 @@ const nodeProto = {
stack.push(childNode);
}
}
},
}
mayContainKind(kind) {
// TODO
return true;
},
}
getParentOfKind(kind) {
let currNode = this.parentNode;
@ -81,7 +85,7 @@ const nodeProto = {
currNode = currNode.parentNode;
}
return null;
},
}
*findAllChildrenOfKind(kind) {
for (const node of this.preorder()) {
@ -96,87 +100,15 @@ const nodeProto = {
}
function isSyntax(value) {
export function isSyntax(value) {
return typeof value === 'object'
&& value !== null
&& value.__NODE_TYPE !== undefined;
}
exported.isSyntax = isSyntax;
let nextNodeId = 1;
function createNode(nodeType) {
const obj = Object.create(nodeProto);
Object.defineProperty(obj, '__NODE_TYPE', {
enumerable: false,
writable: false,
configurable: true,
value: nodeType,
});
Object.defineProperty(obj, 'kind', {
enumerable: false,
configurable: true,
get() {
return this.__NODE_TYPE.index;
}
});
Object.defineProperty(obj, 'errors', {
enumerable: false,
configurable: true,
value: [],
})
Object.defineProperty(obj, 'id', {
enumerable: true,
configurable: true,
value: nextNodeId++,
})
obj.span = null;
return obj;
}
for (const nodeName of Object.keys(NODE_TYPES)) {
exported[`create${nodeName}`] = function (...args) {
const nodeType = NODE_TYPES[nodeName];
const node = createNode(nodeType);
let i = 0;
const iter = nodeType.fields[Symbol.iterator]();
for (; i < args.length; i++) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
node[fieldName] = args[i];
}
while (true) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
throw new Error(`No argument provided for field '${fieldName}'`);
}
if (i < args.length) {
node.span = args[i++];
}
if (i < args.length) {
throw new Error(`Too many arguments provided to function create${nodeName}`);
}
return node;
}
}
exported.setParents = function setParents(node, parentNode = null) {
export function setParents(node, parentNode = null) {
node.parentNode = parentNode;
for (const child of node.getChildNodes()) {
setParents(child, node)
}
}
if (typeof module !== 'undefined') {
module.exports = exports;
}

View file

@ -3,13 +3,13 @@
import * as path from "path"
import * as fs from "fs"
import { parse, SyntaxError } from "../treegen/parser"
import { Declaration } from "../treegen/ast"
import { generateAST } from "../treegen/index"
import { getFileStem } from "../treegen/util"
import { parse, SyntaxError } from "./parser"
import { Declaration } from "./ast"
import { generateAST } from "./index"
import { getFileStem } from "./util"
import minimist from "minimist"
const PACKAGE_ROOT = path.join(__dirname, '..', '..');
//const PACKAGE_ROOT = path.join(__dirname, '..', '..');
const argv = minimist(process.argv.slice(2));

View file

@ -2,13 +2,12 @@
import * as fs from "fs"
import * as path from "path"
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
const PACKAGE_ROOT = path.resolve(__dirname, '..');
const CUSTOM_TYPES = ['Package', 'BoltValue', 'JSValue'];
import { Syntax, Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "./ast"
import { MapLike, assert } from "../util"
import { FileWriter } from "./util"
import { MapLike, FileWriter } from "./util"
export function generateAST(decls: Declaration[]) {
@ -49,45 +48,65 @@ export function generateAST(decls: Declaration[]) {
// Write a JavaScript file that contains all AST definitions.
jsFile.write(`\nconst NODE_TYPES = {\n`);
jsFile.indent();
jsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'snippets', 'ast-before.js'), 'utf8'));
//jsFile.write(`\nconst NODE_TYPES = {\n`);
//jsFile.indent();
for (const decl of finalNodes) {
if (decl.type === 'NodeDeclaration' && isFinalNode(decl.name)) {
jsFile.write(`'${decl.name}': {\n`);
jsFile.write(`class ${decl.name} extends SyntaxBase {\n\n`);
jsFile.indent();
jsFile.write(`index: ${decl.index},\n`);
jsFile.write(`parents: [`);
for (const parentName of getParentChain(decl.name)) {
jsFile.write(`'${parentName}', `)
}
jsFile.write(`'Syntax'],\n`);
jsFile.write(`fields: new Map([\n`);
jsFile.write(`static kind = ${decl.index};\n\n`);
jsFile.write(`static parents = `);
jsFile.write(JSON.stringify([...getParentChain(decl.name), 'Syntax'], undefined, 2));
jsFile.write(';\n\n')
//jsFile.write(`static fields = new Map([\n`);
//jsFile.indent();
//jsFile.write(JSON.stringify([...getAllFields(decl)].map(field => [field.name, jsonify(field.typeNode)]), undefined, 2)),
//jsFile.dedent();
jsFile.write(`constructor(\n`);
jsFile.indent();
for (const field of getAllFields(decl)) {
jsFile.write(`['${field.name}', ${JSON.stringify(jsonify(field.typeNode))}],\n`);
jsFile.write(`${field.name},\n`);
}
jsFile.write(`span = null,\n`)
jsFile.dedent();
jsFile.write(']),\n');
jsFile.dedent();
jsFile.write('},\n');
}
jsFile.write(`) {\n`);
jsFile.indent();
jsFile.write(`super(span);\n`);
for (const field of getAllFields(decl)) {
jsFile.write(`this.${field.name} = ${field.name};\n`);
}
jsFile.write(`this.span = span\n`)
jsFile.dedent();
jsFile.write('};\n\n');
jsFile.write('}\n\n')
jsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'src', 'treegen', 'ast-template.js'), 'utf8'));
jsFile.dedent();
jsFile.write('}\n');
}
jsFile.write(`function kindToString (kind) {\n switch (kind) {\n`);
jsFile.write(`const NODE_CLASSES = {\n`)
jsFile.indent();
for (const node of finalNodes) {
jsFile.write(node.name + ',\n');
}
jsFile.dedent();
jsFile.write('}\n\n')
//jsFile.dedent();
//jsFile.write('};\n\n');
jsFile.write(`export function kindToString (kind) {\n switch (kind) {\n`);
jsFile.indent(2);
for (const leafNode of finalNodes) {
jsFile.write(`case ${leafNode.index}: return '${leafNode.name}';\n`);
}
jsFile.dedent(2);
jsFile.write(` }\n}\n\n`);
jsFile.write('exported.kindToString = kindToString;\n\n');
for (const decl of nodeDecls) {
jsFile.write(`exported.is${decl.name} = function (value) {\n`);
jsFile.write(`export function is${decl.name}(value) {\n`);
jsFile.indent();
jsFile.write(`if (!isSyntax(value)) {\n return false;\n}\n`);
if (isFinalNode(decl.name)) {
@ -99,11 +118,11 @@ export function generateAST(decls: Declaration[]) {
jsFile.write(`}\n`);
}
jsFile.write(`\nif (typeof module !== 'undefined') {\n module.exports = exported;\n}\n\n`)
jsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'snippets', 'ast-after.js'), 'utf8'));
// Write corresponding TypeScript declarations
dtsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'src', 'treegen', 'ast.dts.template'), 'utf8'));
dtsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'snippets', 'ast-before.d.ts'), 'utf8'));
dtsFile.write('export class NodeVisitor {\n');
dtsFile.write(' public visit(node: Syntax): void;\n');
@ -219,6 +238,8 @@ export function generateAST(decls: Declaration[]) {
dtsFile.write(`export function is${decl.name}(value: any): value is ${decl.name};\n`);
}
dtsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'snippets', 'ast-after.d.ts'), 'utf8'));
return {
jsFile: jsFile.currentText,
dtsFile: dtsFile.currentText,

View file

@ -5,6 +5,8 @@ export function getFileStem(filepath: string): string {
return path.basename(filepath).split('.')[0];
}
export interface MapLike<T> { [key: string]: T }
function isWhiteSpace(ch: string) {
return /[\r\t ]/.test(ch);
}

12
treegen/tsconfig.json Normal file
View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"outDir": "./lib",
"strict": true,
"esModuleInterop": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}