Move 'treegen' to seperate directory
This commit is contained in:
parent
4251c6eeaf
commit
dd53ca87f3
14 changed files with 132 additions and 120 deletions
11
treegen/Makefile
Normal file
11
treegen/Makefile
Normal 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
5
treegen/package-lock.json
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "treegen",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
29
treegen/package.json
Normal file
29
treegen/package.json
Normal 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
0
treegen/snippets/ast-after.d.ts
vendored
Normal file
0
treegen/snippets/ast-after.js
Normal file
0
treegen/snippets/ast-after.js
Normal 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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
@ -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,
|
|
@ -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
12
treegen/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
Reference in a new issue