diff --git a/treegen/src/bin/treegen.ts b/treegen/src/bin/treegen.ts new file mode 100755 index 000000000..d9711bcf2 --- /dev/null +++ b/treegen/src/bin/treegen.ts @@ -0,0 +1,74 @@ +#!/usr/bin/env node + +import * as path from "path" +import * as fs from "fs" + +import { parse, SyntaxError } from "../parser" +import { Declaration } from "../ast" + +for (const filename of process.argv.slice(2)) { + const contents = fs.readFileSync(filename, 'utf8'); + let decls: Declaration[]; + try { + decls = parse(contents); + } catch (e) { + if (e instanceof SyntaxError) { + console.error(`${filename}:${e.location.start.line}:${e.location.start.column}: ${e.message}`); + process.exit(1); + } else { + throw e; + } + } + console.error(jsonify(decls)); + const generated = generateAST(decls); + fs.writeFileSync(getFileStem(filename).toLowerCase(), generated, 'utf8'); +} + +interface FastStringMap { [key: string]: T } + +function generateAST(decls: Declaration[]) { + + const declByName: FastStringMap = Object.create(null); + for (const decl of decls) { + declByName[decl.name] = decl; + } + + return '' + +} + +function jsonify(value: any) { + + function visitNode(node: any) { + + const obj: any = {}; + + for (const key of Object.keys(node)) { + if (key !== 'type' && key !== 'span') { + const value = node[key]; + if (Array.isArray(value)) { + obj[key] = value.map(visit); + } else { + obj[key] = visit(value); + } + } + } + + return obj; + } + + function visit(value: any) { + if (value.__IS_NODE) { + return visitNode(value); + } else { + return value; + } + } + + return visit(value); +} + +function getFileStem(filepath: string) { + return path.basename(filepath).split('.')[0]; +} + diff --git a/treegen/src/parser.pegjs b/treegen/src/parser.pegjs index 3b0b090a0..cb5d19afd 100644 --- a/treegen/src/parser.pegjs +++ b/treegen/src/parser.pegjs @@ -1,6 +1,26 @@ +{ + + function liftArray(value) { + if (Array.isArray(value)) { + return value; + } + return value === null || value === undefined ? [] : [value] + } + + function createNode(type, props) { + return { + __IS_NODE: true, + type, + span: location(), + ...props, + } + } + +} + File - = __ (@Declaration __)* + = __ @(@Declaration __)* Declaration = NodeDeclaration @@ -8,34 +28,52 @@ Declaration / TypeDeclaration NodeDeclaration - = NodeToken __ name:Identifier parents:(__ '>' __ ExtendsList)? fields:(__ '{' __ (@NodeField __)* '}')? EOS + = NodeToken __ name:Identifier parents:(__ '>' __ ExtendsList)? fields:(__ '{' __ (@NodeField __)* '}')? EOS { + return createNode('NodeDeclaration', { name, parents: liftArray(parents), fields: liftArray(fields) }); + } ExtendsList - = head:Identifier tail:(__ ',' __ @Identifier)* + = head:Identifier tail:(__ ',' __ @Identifier)* { + return [head, ...tail]; + } NodeField - = name:Identifier __ ':' __ type:TypeNode EOD + = name:Identifier __ ':' __ typeNode:TypeNode EOD { + return createNode('NodeField', { name, typeNode }); + } TypeNode = UnionTypeNode UnionTypeNode - = head:ReferenceTypeNode tail:(__ '|' __ @ReferenceTypeNode)* + = head:ReferenceTypeNode tail:(__ '|' __ @ReferenceTypeNode)* { + return [head, ...tail]; + } ReferenceTypeNode - = name:Identifier genericArgs:(__ '<' __ @TypeNodeList __ '>')? + = name:Identifier genericArgs:(__ '<' __ @TypeNodeList __ '>')? { + return createNode('ReferenceTypeNode', { name, genericArgs }); + } TypeNodeList - = head:TypeNode tail:(__ ',' __ @TypeNode)* + = parsed:(TypeNode (__ ',' __ @TypeNode)*)? { + return parsed !== null ? [parsed[0], ...parsed[1]] : []; + } EnumDeclaration - = EnumToken __ name:Identifier __ '{' __ (@EnumField __)* '}' EOS + = EnumToken __ name:Identifier __ '{' __ fields:(@EnumField __)* '}' EOS { + return createNode('EnumDeclaration', { name, fields }); + } EnumField - = name:Identifier value:(__ '=' __ @Integer)? EOD + = name:Identifier value:(__ '=' __ @Integer)? EOD { + return createNode('EnumField', { name, value }); + } TypeDeclaration - = TypeToken __ name:Identifier __ '=' __ type:TypeNode EOS + = TypeToken __ name:Identifier __ '=' __ typeNode:TypeNode EOS { + return createNode('TypeDeclaration', { name, typeNode }); + } Identifier "an identifier" = $(IdentifierStart IdentifierPart*)