diff --git a/.gitignore b/.gitignore index 5abf6d87c4..b7cb658085 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ docs/ node_modules/ out/ raw/ -.history \ No newline at end of file +.history +lib/visitor/dist/lib/ + +lib/visitor/dist/src/ diff --git a/bin/asinit b/bin/asinit old mode 100644 new mode 100755 diff --git a/cli/asc.js b/cli/asc.js index 4ee989d100..f869400c9a 100644 --- a/cli/asc.js +++ b/cli/asc.js @@ -212,19 +212,31 @@ exports.main = function main(argv, options, callback) { // Set up transforms const transforms = []; if (args.transform) { - args.transform.forEach(transform => - transforms.push( - require( - path.isAbsolute(transform = transform.trim()) - ? transform - : path.join(process.cwd(), transform) - ) - ) - ); + args.transform.forEach(transform => { + transforms.push(require(path.isAbsolute(transform = transform.trim()) ? + transform : path.join(process.cwd(), transform))); + }); } function applyTransform(name, ...args) { transforms.forEach(transform => { - if (typeof transform[name] === "function") transform[name](...args); + try { + // Export transform function function + if (transform[name] && typeof transform[name] === "function") { + transform[name](...args); + } else if (transform.default) { + // Export default class which needs to be constructor + if (!isConstructor(transform.default)){ + throw new Error("default exported transformer must be a constructor"); + } + const transformer = new transform.default(...args); + if (typeof transformer[name] !== "function") { + throw new Error("Transformer missing " + name + " method."); + } + transformer[name](); + } + } catch (e) { + callback(e); + } }); } @@ -426,7 +438,7 @@ exports.main = function main(argv, options, callback) { } // Call afterParse transform hook - applyTransform("afterParse", parser); + applyTransform("afterParse", parser, writeFile, readFile, baseDir, writeStdout, writeStderr); // Parse additional files, if any { @@ -796,6 +808,20 @@ exports.main = function main(argv, options, callback) { } }); } + + function writeStderr(contents) { + if (!writeStderr.used) { + stats.writeCount++; + writeStderr.used = true; + } + stats.writeTime += measure(() => { + if (typeof contents === "string") { + stderr.write(contents, { encoding: "utf8" }); + } else { + stderr.write(contents); + } + }); + } } /** Checks diagnostics emitted so far for errors. */ @@ -939,3 +965,7 @@ exports.tscOptions = { types: [], allowJs: false }; + +function isConstructor(func) { + return (func && typeof func === "function" && func.prototype && func.prototype.constructor) === func; +} diff --git a/cli/transform.d.ts b/cli/transform.d.ts index 9bc060b6df..e6616cd4ba 100644 --- a/cli/transform.d.ts +++ b/cli/transform.d.ts @@ -5,7 +5,8 @@ import { Parser } from "../src/parser"; +declare function writeFile(name: string, contents: string | Uint8Array, baseDir: string): void; export interface Transform { /** Called when parsing is complete, before a program is instantiated from the AST. */ - afterParse(parser: Parser): void; + afterParse(parser: Parser, writer?: typeof writeFile, baseDir?: string): void; } diff --git a/lib/transformer/README.md b/lib/transformer/README.md new file mode 100644 index 0000000000..e9c5e81a36 --- /dev/null +++ b/lib/transformer/README.md @@ -0,0 +1,94 @@ +# AST Transformer + +After parsing the files imported by the entry files, the Abstract Syntax Tree (AST) is made. With the `--transform` argument, the compiler will load any js files provided and if the exports include a Transformer class. + +## Transformer Class + +By extending the Transformer class your subclass gains access to the `Parser`, `writeFile` function, `baseDir` is where the project's root is located, and `stdout` and `stderr` + +```ts +export abstract class Transformer { + constructor( + protected parser: Parser, + protected writeFile: FileWriter, + protected baseDir: string, + protected stdout: (data: string) => void, + protected stderr: (data: string) => void + ) {} + + get program(): Program { + return this.parser.program; + } + + abstract afterParse(): void; +} +``` + +### Example ASTPrinter + +Below is an example of visiting each source that is a user entry file and creating a string from visiting the AST. + +```ts +/** + * Example of using a transformer to print the AST for each entry file + */ +export default class Printer extends Transformer { + afterParse(): void { + const files = this.parser.program.sources.filter( + _source => _source.sourceKind == SourceKind.USER_ENTRY + ); + files.forEach(source => { + // Create a string of source rebuilt from the AST node `source` + const sourceText: string = ASTBuilder.build(source); + this.stdout(sourceText); + }); + } +} +``` + +To try it out first you might need to build the transformer files: + +```bash +npm run build +``` + +Then in the test directory build the sample assemblyscript file: + +```bash +cd tests && npm run build +``` + +## Visitors + +There is a base AST visitor class in `src/base.ts` which visits each node in the AST. If extended, overwritten methods visit methods will be called when traversing the tree. + +For example, `src/examples/functions.ts` collects the names of function and method declarations. + +```ts +class FunctionVisitor extends BaseVisitor { + funcsFound: string[] = []; + currentClass: string; + + visitClassDeclaration(node: ClassDeclaration): void { + // Remember current class + this.currentClass = node.name.text; + // Important to pass call parent visitor if you want to visit child nodes + super.visitClassDeclaration(node); + // Note can't call `super.visit(node) because it will call this function again. + } + + visitFunctionDeclaration(node: FunctionDeclaration): void { + this.funcsFound.push("Function: " + node.name.text); + } + + visitMethodDeclaration(node: MethodDeclaration): void { + this.funcsFound.push("Method: " + this.currentClass + "." + node.name.text); + } + + static visit(node: Node): string { + const visitor = new FunctionVisitor(); + visitor.visit(node); + return visitor.funcsFound.join("\n"); + } +} +``` diff --git a/lib/transformer/dist/ASTPrinter.js b/lib/transformer/dist/ASTPrinter.js new file mode 100644 index 0000000000..4b477391ee --- /dev/null +++ b/lib/transformer/dist/ASTPrinter.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.transformer=e():t.transformer=e()}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function i(s){if(e[s])return e[s].exports;var r=e[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=e,i.d=function(t,e,s){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(i.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(s,r,function(e){return t[e]}.bind(null,r));return s},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=2)}([function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){t[t.SOURCE=0]="SOURCE",t[t.NAMEDTYPE=1]="NAMEDTYPE",t[t.FUNCTIONTYPE=2]="FUNCTIONTYPE",t[t.TYPENAME=3]="TYPENAME",t[t.TYPEPARAMETER=4]="TYPEPARAMETER",t[t.PARAMETER=5]="PARAMETER",t[t.IDENTIFIER=6]="IDENTIFIER",t[t.ASSERTION=7]="ASSERTION",t[t.BINARY=8]="BINARY",t[t.CALL=9]="CALL",t[t.CLASS=10]="CLASS",t[t.COMMA=11]="COMMA",t[t.ELEMENTACCESS=12]="ELEMENTACCESS",t[t.FALSE=13]="FALSE",t[t.FUNCTION=14]="FUNCTION",t[t.INSTANCEOF=15]="INSTANCEOF",t[t.LITERAL=16]="LITERAL",t[t.NEW=17]="NEW",t[t.NULL=18]="NULL",t[t.PARENTHESIZED=19]="PARENTHESIZED",t[t.PROPERTYACCESS=20]="PROPERTYACCESS",t[t.TERNARY=21]="TERNARY",t[t.SUPER=22]="SUPER",t[t.THIS=23]="THIS",t[t.TRUE=24]="TRUE",t[t.CONSTRUCTOR=25]="CONSTRUCTOR",t[t.UNARYPOSTFIX=26]="UNARYPOSTFIX",t[t.UNARYPREFIX=27]="UNARYPREFIX",t[t.BLOCK=28]="BLOCK",t[t.BREAK=29]="BREAK",t[t.CONTINUE=30]="CONTINUE",t[t.DO=31]="DO",t[t.EMPTY=32]="EMPTY",t[t.EXPORT=33]="EXPORT",t[t.EXPORTDEFAULT=34]="EXPORTDEFAULT",t[t.EXPORTIMPORT=35]="EXPORTIMPORT",t[t.EXPRESSION=36]="EXPRESSION",t[t.FOR=37]="FOR",t[t.IF=38]="IF",t[t.IMPORT=39]="IMPORT",t[t.RETURN=40]="RETURN",t[t.SWITCH=41]="SWITCH",t[t.THROW=42]="THROW",t[t.TRY=43]="TRY",t[t.VARIABLE=44]="VARIABLE",t[t.VOID=45]="VOID",t[t.WHILE=46]="WHILE",t[t.CLASSDECLARATION=47]="CLASSDECLARATION",t[t.ENUMDECLARATION=48]="ENUMDECLARATION",t[t.ENUMVALUEDECLARATION=49]="ENUMVALUEDECLARATION",t[t.FIELDDECLARATION=50]="FIELDDECLARATION",t[t.FUNCTIONDECLARATION=51]="FUNCTIONDECLARATION",t[t.IMPORTDECLARATION=52]="IMPORTDECLARATION",t[t.INDEXSIGNATUREDECLARATION=53]="INDEXSIGNATUREDECLARATION",t[t.INTERFACEDECLARATION=54]="INTERFACEDECLARATION",t[t.METHODDECLARATION=55]="METHODDECLARATION",t[t.NAMESPACEDECLARATION=56]="NAMESPACEDECLARATION",t[t.TYPEDECLARATION=57]="TYPEDECLARATION",t[t.VARIABLEDECLARATION=58]="VARIABLEDECLARATION",t[t.DECORATOR=59]="DECORATOR",t[t.EXPORTMEMBER=60]="EXPORTMEMBER",t[t.SWITCHCASE=61]="SWITCHCASE",t[t.COMMENT=62]="COMMENT"}(e.NodeKind||(e.NodeKind={})),function(t){t[t.DEFAULT=0]="DEFAULT",t[t.OPTIONAL=1]="OPTIONAL",t[t.REST=2]="REST"}(e.ParameterKind||(e.ParameterKind={})),function(t){t[t.CUSTOM=0]="CUSTOM",t[t.GLOBAL=1]="GLOBAL",t[t.OPERATOR=2]="OPERATOR",t[t.OPERATOR_BINARY=3]="OPERATOR_BINARY",t[t.OPERATOR_PREFIX=4]="OPERATOR_PREFIX",t[t.OPERATOR_POSTFIX=5]="OPERATOR_POSTFIX",t[t.UNMANAGED=6]="UNMANAGED",t[t.SEALED=7]="SEALED",t[t.INLINE=8]="INLINE",t[t.EXTERNAL=9]="EXTERNAL",t[t.BUILTIN=10]="BUILTIN",t[t.LAZY=11]="LAZY",t[t.UNSAFE=12]="UNSAFE"}(e.DecoratorKind||(e.DecoratorKind={})),e.DecoratorKind||(e.DecoratorKind={}),function(t){t[t.LINE=0]="LINE",t[t.TRIPLE=1]="TRIPLE",t[t.BLOCK=2]="BLOCK"}(e.CommentKind||(e.CommentKind={})),function(t){t[t.FLOAT=0]="FLOAT",t[t.INTEGER=1]="INTEGER",t[t.STRING=2]="STRING",t[t.REGEXP=3]="REGEXP",t[t.ARRAY=4]="ARRAY",t[t.OBJECT=5]="OBJECT"}(e.LiteralKind||(e.LiteralKind={})),function(t){t[t.PREFIX=0]="PREFIX",t[t.AS=1]="AS",t[t.NONNULL=2]="NONNULL"}(e.AssertionKind||(e.AssertionKind={})),function(t){t[t.USER=0]="USER",t[t.USER_ENTRY=1]="USER_ENTRY",t[t.LIBRARY=2]="LIBRARY",t[t.LIBRARY_ENTRY=3]="LIBRARY_ENTRY"}(e.SourceKind||(e.SourceKind={})),function(t){t[t.NONE=0]="NONE",t[t.ARROW_PARENTHESIZED=1]="ARROW_PARENTHESIZED",t[t.ARROW_SINGLE=2]="ARROW_SINGLE"}(e.ArrowKind||(e.ArrowKind={})),e.findDecorator=function(t,e){if(e)for(var i=0,s=e.length;i")}t.isNullable&&s.push(" | null")}},t.prototype.visitFunctionTypeNode=function(t){var e=t.isNullable,i=this.sb;i.push(e?"((":"(");var s=t.explicitThisType;s&&(i.push("this: "),this.visitTypeNode(s));var r=t.parameters,n=r.length;if(n){s&&i.push(", "),this.serializeParameter(r[0]);for(var a=1;a "),this.visitTypeNode(o)):i.push(") => void"),e&&i.push(") | null")},t.prototype.visitTypeParameter=function(t){this.visitIdentifierExpression(t.name);var e=t.extendsType;e&&(this.sb.push(" extends "),this.visitTypeNode(e));var i=t.defaultType;i&&(this.sb.push("="),this.visitTypeNode(i))},t.prototype.visitIdentifierExpression=function(t){t.isQuoted?this.visitStringLiteral(t.text):this.sb.push(t.text)},t.prototype.visitArrayLiteralExpression=function(t){var e=this.sb;e.push("[");var i=t.elementExpressions,s=i.length;if(s){i[0]&&this.visitNode(i[0]);for(var r=1;r"),this.visitNode(t.expression);break;case n.AssertionKind.AS:this.visitNode(t.expression),e.push(" as "),this.visitTypeNode(assert(t.toType));break;case n.AssertionKind.NONNULL:this.visitNode(t.expression),e.push("!");break;default:assert(!1)}},t.prototype.visitBinaryExpression=function(t){var e=this.sb;this.visitNode(t.left),e.push(" "),e.push(r.operatorTokenToString(t.operator)),e.push(" "),this.visitNode(t.right)},t.prototype.visitCallExpression=function(t){var e=this.sb;this.visitNode(t.expression);var i=t.typeArguments;if(i){var s=i.length;if(s){e.push("<"),this.visitTypeNode(i[0]);for(var r=1;r(")}}else e.push("(");var n=t.arguments,a=n.length;if(a){this.visitNode(n[0]);for(r=1;rs&&i.push(t.substring(s,s=n+1)),i.push("\\0"),s=++n;break;case 8:n>s&&i.push(t.substring(s,n)),s=++n,i.push("\\b");break;case 9:n>s&&i.push(t.substring(s,n)),s=++n,i.push("\\t");break;case 10:n>s&&i.push(t.substring(s,n)),s=++n,i.push("\\n");break;case 11:n>s&&i.push(t.substring(s,n)),s=++n,i.push("\\v");break;case 12:n>s&&i.push(t.substring(s,n)),s=++n,i.push("\\f");break;case 13:n>s&&i.push(t.substring(s,n)),i.push("\\r"),s=++n;break;case 34:e?++n:(n>s&&i.push(t.substring(s,n)),i.push('\\"'),s=++n);break;case 39:e?(n>s&&i.push(t.substring(s,n)),i.push("\\'"),s=++n):++n;break;case 92:n>s&&i.push(t.substring(s,n)),i.push("\\\\"),s=++n;break;default:++n}n>s&&i.push(t.substring(s,n)),i.push(r)},t.prototype.visitStringLiteralExpression=function(t){this.visitStringLiteral(t.value)},t.prototype.visitRegexpLiteralExpression=function(t){var e=this.sb;e.push("/"),e.push(t.pattern),e.push("/"),e.push(t.patternFlags)},t.prototype.visitNewExpression=function(t){this.sb.push("new "),this.visitCallExpression(t)},t.prototype.visitParenthesizedExpression=function(t){var e=this.sb;e.push("("),this.visitNode(t.expression),e.push(")")},t.prototype.visitPropertyAccessExpression=function(t){this.visitNode(t.expression),this.sb.push("."),this.visitIdentifierExpression(t.property)},t.prototype.visitTernaryExpression=function(t){var e=this.sb;this.visitNode(t.condition),e.push(" ? "),this.visitNode(t.ifThen),e.push(" : "),this.visitNode(t.ifElse)},t.prototype.visitUnaryExpression=function(t){switch(t.kind){case 26:this.visitUnaryPostfixExpression(t);break;case 27:this.visitUnaryPrefixExpression(t);break;default:assert(!1)}},t.prototype.visitUnaryPostfixExpression=function(t){this.visitNode(t.operand),this.sb.push(r.operatorTokenToString(t.operator))},t.prototype.visitUnaryPrefixExpression=function(t){this.sb.push(r.operatorTokenToString(t.operator)),this.visitNode(t.operand)},t.prototype.visitNodeAndTerminate=function(t){this.visitNode(t);var e=this.sb;if(e.length&&44!=t.kind&&36!=t.kind){var i=e[e.length-1],s=i.length-1;s>=0&&(125==i.charCodeAt(s)||59==i.charCodeAt(s))?e.push("\n"):e.push(";\n")}else e.push(";\n")},t.prototype.visitBlockStatement=function(t){var e=this.sb,i=t.statements,r=i.length;if(r){e.push("{\n");for(var n=++this.indentLevel,a=0;a")}var E=t.extendsType;E&&(a.push(" extends "),this.visitTypeNode(E));var h=t.implementsTypes;if(h){var p=h.length;if(p){a.push(" implements "),this.visitTypeNode(h[0]);for(r=1;r")}}if(2==t.arrowKind){var o=i.parameters;assert(1==o.length),assert(!i.explicitThisType),this.serializeParameter(o[0])}else{e.push("(");var E=(o=i.parameters).length,h=i.explicitThisType;if(h&&(e.push("this: "),this.visitTypeNode(h)),E){h&&e.push(", "),this.serializeParameter(o[0]);for(a=1;a "),this.visitNode(p)):(assert(!n.isTypeOmitted(A)),e.push(" => "),this.visitTypeNode(A)):(n.isTypeOmitted(A)||t.isAny(266240)?e.push(")"):(e.push("): "),this.visitTypeNode(A)),p&&(e.push(" "),this.visitNode(p)))},t.prototype.visitIfStatement=function(t){var e=this.sb;e.push("if ("),this.visitNode(t.condition),e.push(") ");var i=t.ifTrue;this.visitNode(i),28!=i.kind&&e.push(";\n");var s=t.ifFalse;s&&(28==i.kind?e.push(" else "):e.push("else "),this.visitNode(s))},t.prototype.visitImportDeclaration=function(t){var e=t.foreignName,i=t.name;this.visitIdentifierExpression(e),e.text!=i.text&&(this.sb.push(" as "),this.visitIdentifierExpression(i))},t.prototype.visitImportStatement=function(t){var e=this.sb;e.push("import ");var i=t.declarations,r=t.namespaceName;if(i){var n=i.length;if(n){e.push("{\n");var a=++this.indentLevel;s.indent(e,a),this.visitImportDeclaration(i[0]);for(var o=1;o")}var E=t.extendsType;E&&(a.push(" extends "),this.visitTypeNode(E)),a.push(" {\n");var h=++this.indentLevel,p=t.members;for(r=0,n=p.length;r")}}r.push(" = "),this.visitTypeNode(t.type)},t.prototype.visitVariableDeclaration=function(t){this.visitIdentifierExpression(t.name);var e=t.type,i=this.sb;8192&t.flags&&i.push("!"),e&&(i.push(": "),this.visitTypeNode(e));var s=t.initializer;s&&(i.push(" = "),this.visitNode(s))},t.prototype.visitVariableStatement=function(t){var e=t.decorators;if(e)for(var i=0,s=e.length;i=4;)t.push(n),e-=4;e>=2&&(t.push(r),e-=2),e&&t.push(s)},function(t){t[t.NULL=0]="NULL",t[t.LINEFEED=10]="LINEFEED",t[t.CARRIAGERETURN=13]="CARRIAGERETURN",t[t.LINESEPARATOR=8232]="LINESEPARATOR",t[t.PARAGRAPHSEPARATOR=8233]="PARAGRAPHSEPARATOR",t[t.NEXTLINE=133]="NEXTLINE",t[t.SPACE=32]="SPACE",t[t.NONBREAKINGSPACE=160]="NONBREAKINGSPACE",t[t.ENQUAD=8192]="ENQUAD",t[t.EMQUAD=8193]="EMQUAD",t[t.ENSPACE=8194]="ENSPACE",t[t.EMSPACE=8195]="EMSPACE",t[t.THREEPEREMSPACE=8196]="THREEPEREMSPACE",t[t.FOURPEREMSPACE=8197]="FOURPEREMSPACE",t[t.SIXPEREMSPACE=8198]="SIXPEREMSPACE",t[t.FIGURESPACE=8199]="FIGURESPACE",t[t.PUNCTUATIONSPACE=8200]="PUNCTUATIONSPACE",t[t.THINSPACE=8201]="THINSPACE",t[t.HAIRSPACE=8202]="HAIRSPACE",t[t.ZEROWIDTHSPACE=8203]="ZEROWIDTHSPACE",t[t.NARROWNOBREAKSPACE=8239]="NARROWNOBREAKSPACE",t[t.IDEOGRAPHICSPACE=12288]="IDEOGRAPHICSPACE",t[t.MATHEMATICALSPACE=8287]="MATHEMATICALSPACE",t[t.OGHAM=5760]="OGHAM",t[t._=95]="_",t[t._0=48]="_0",t[t._1=49]="_1",t[t._2=50]="_2",t[t._3=51]="_3",t[t._4=52]="_4",t[t._5=53]="_5",t[t._6=54]="_6",t[t._7=55]="_7",t[t._8=56]="_8",t[t._9=57]="_9",t[t.a=97]="a",t[t.b=98]="b",t[t.c=99]="c",t[t.d=100]="d",t[t.e=101]="e",t[t.f=102]="f",t[t.g=103]="g",t[t.h=104]="h",t[t.i=105]="i",t[t.j=106]="j",t[t.k=107]="k",t[t.l=108]="l",t[t.m=109]="m",t[t.n=110]="n",t[t.o=111]="o",t[t.p=112]="p",t[t.q=113]="q",t[t.r=114]="r",t[t.s=115]="s",t[t.t=116]="t",t[t.u=117]="u",t[t.v=118]="v",t[t.w=119]="w",t[t.x=120]="x",t[t.y=121]="y",t[t.z=122]="z",t[t.A=65]="A",t[t.B=66]="B",t[t.C=67]="C",t[t.D=68]="D",t[t.E=69]="E",t[t.F=70]="F",t[t.G=71]="G",t[t.H=72]="H",t[t.I=73]="I",t[t.J=74]="J",t[t.K=75]="K",t[t.L=76]="L",t[t.M=77]="M",t[t.N=78]="N",t[t.O=79]="O",t[t.P=80]="P",t[t.Q=81]="Q",t[t.R=82]="R",t[t.S=83]="S",t[t.T=84]="T",t[t.U=85]="U",t[t.V=86]="V",t[t.W=87]="W",t[t.X=88]="X",t[t.Y=89]="Y",t[t.Z=90]="Z",t[t.AMPERSAND=38]="AMPERSAND",t[t.ASTERISK=42]="ASTERISK",t[t.AT=64]="AT",t[t.BACKSLASH=92]="BACKSLASH",t[t.BACKTICK=96]="BACKTICK",t[t.BAR=124]="BAR",t[t.CARET=94]="CARET",t[t.CLOSEBRACE=125]="CLOSEBRACE",t[t.CLOSEBRACKET=93]="CLOSEBRACKET",t[t.CLOSEPAREN=41]="CLOSEPAREN",t[t.COLON=58]="COLON",t[t.COMMA=44]="COMMA",t[t.DOLLAR=36]="DOLLAR",t[t.DOT=46]="DOT",t[t.DOUBLEQUOTE=34]="DOUBLEQUOTE",t[t.EQUALS=61]="EQUALS",t[t.EXCLAMATION=33]="EXCLAMATION",t[t.GREATERTHAN=62]="GREATERTHAN",t[t.HASH=35]="HASH",t[t.LESSTHAN=60]="LESSTHAN",t[t.MINUS=45]="MINUS",t[t.OPENBRACE=123]="OPENBRACE",t[t.OPENBRACKET=91]="OPENBRACKET",t[t.OPENPAREN=40]="OPENPAREN",t[t.PERCENT=37]="PERCENT",t[t.PLUS=43]="PLUS",t[t.QUESTION=63]="QUESTION",t[t.SEMICOLON=59]="SEMICOLON",t[t.SINGLEQUOTE=39]="SINGLEQUOTE",t[t.SLASH=47]="SLASH",t[t.TILDE=126]="TILDE",t[t.BACKSPACE=8]="BACKSPACE",t[t.FORMFEED=12]="FORMFEED",t[t.BYTEORDERMARK=65279]="BYTEORDERMARK",t[t.TAB=9]="TAB",t[t.VERTICALTAB=11]="VERTICALTAB"}(e.CharCode||(e.CharCode={}))},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){t[t.ABSTRACT=0]="ABSTRACT",t[t.AS=1]="AS",t[t.ASYNC=2]="ASYNC",t[t.AWAIT=3]="AWAIT",t[t.BREAK=4]="BREAK",t[t.CASE=5]="CASE",t[t.CATCH=6]="CATCH",t[t.CLASS=7]="CLASS",t[t.CONST=8]="CONST",t[t.CONTINUE=9]="CONTINUE",t[t.CONSTRUCTOR=10]="CONSTRUCTOR",t[t.DEBUGGER=11]="DEBUGGER",t[t.DECLARE=12]="DECLARE",t[t.DEFAULT=13]="DEFAULT",t[t.DELETE=14]="DELETE",t[t.DO=15]="DO",t[t.ELSE=16]="ELSE",t[t.ENUM=17]="ENUM",t[t.EXPORT=18]="EXPORT",t[t.EXTENDS=19]="EXTENDS",t[t.FALSE=20]="FALSE",t[t.FINALLY=21]="FINALLY",t[t.FOR=22]="FOR",t[t.FROM=23]="FROM",t[t.FUNCTION=24]="FUNCTION",t[t.GET=25]="GET",t[t.IF=26]="IF",t[t.IMPLEMENTS=27]="IMPLEMENTS",t[t.IMPORT=28]="IMPORT",t[t.IN=29]="IN",t[t.INSTANCEOF=30]="INSTANCEOF",t[t.INTERFACE=31]="INTERFACE",t[t.IS=32]="IS",t[t.KEYOF=33]="KEYOF",t[t.LET=34]="LET",t[t.MODULE=35]="MODULE",t[t.NAMESPACE=36]="NAMESPACE",t[t.NEW=37]="NEW",t[t.NULL=38]="NULL",t[t.OF=39]="OF",t[t.PACKAGE=40]="PACKAGE",t[t.PRIVATE=41]="PRIVATE",t[t.PROTECTED=42]="PROTECTED",t[t.PUBLIC=43]="PUBLIC",t[t.READONLY=44]="READONLY",t[t.RETURN=45]="RETURN",t[t.SET=46]="SET",t[t.STATIC=47]="STATIC",t[t.SUPER=48]="SUPER",t[t.SWITCH=49]="SWITCH",t[t.THIS=50]="THIS",t[t.THROW=51]="THROW",t[t.TRUE=52]="TRUE",t[t.TRY=53]="TRY",t[t.TYPE=54]="TYPE",t[t.TYPEOF=55]="TYPEOF",t[t.VAR=56]="VAR",t[t.VOID=57]="VOID",t[t.WHILE=58]="WHILE",t[t.WITH=59]="WITH",t[t.YIELD=60]="YIELD",t[t.OPENBRACE=61]="OPENBRACE",t[t.CLOSEBRACE=62]="CLOSEBRACE",t[t.OPENPAREN=63]="OPENPAREN",t[t.CLOSEPAREN=64]="CLOSEPAREN",t[t.OPENBRACKET=65]="OPENBRACKET",t[t.CLOSEBRACKET=66]="CLOSEBRACKET",t[t.DOT=67]="DOT",t[t.DOT_DOT_DOT=68]="DOT_DOT_DOT",t[t.SEMICOLON=69]="SEMICOLON",t[t.COMMA=70]="COMMA",t[t.LESSTHAN=71]="LESSTHAN",t[t.GREATERTHAN=72]="GREATERTHAN",t[t.LESSTHAN_EQUALS=73]="LESSTHAN_EQUALS",t[t.GREATERTHAN_EQUALS=74]="GREATERTHAN_EQUALS",t[t.EQUALS_EQUALS=75]="EQUALS_EQUALS",t[t.EXCLAMATION_EQUALS=76]="EXCLAMATION_EQUALS",t[t.EQUALS_EQUALS_EQUALS=77]="EQUALS_EQUALS_EQUALS",t[t.EXCLAMATION_EQUALS_EQUALS=78]="EXCLAMATION_EQUALS_EQUALS",t[t.EQUALS_GREATERTHAN=79]="EQUALS_GREATERTHAN",t[t.PLUS=80]="PLUS",t[t.MINUS=81]="MINUS",t[t.ASTERISK_ASTERISK=82]="ASTERISK_ASTERISK",t[t.ASTERISK=83]="ASTERISK",t[t.SLASH=84]="SLASH",t[t.PERCENT=85]="PERCENT",t[t.PLUS_PLUS=86]="PLUS_PLUS",t[t.MINUS_MINUS=87]="MINUS_MINUS",t[t.LESSTHAN_LESSTHAN=88]="LESSTHAN_LESSTHAN",t[t.GREATERTHAN_GREATERTHAN=89]="GREATERTHAN_GREATERTHAN",t[t.GREATERTHAN_GREATERTHAN_GREATERTHAN=90]="GREATERTHAN_GREATERTHAN_GREATERTHAN",t[t.AMPERSAND=91]="AMPERSAND",t[t.BAR=92]="BAR",t[t.CARET=93]="CARET",t[t.EXCLAMATION=94]="EXCLAMATION",t[t.TILDE=95]="TILDE",t[t.AMPERSAND_AMPERSAND=96]="AMPERSAND_AMPERSAND",t[t.BAR_BAR=97]="BAR_BAR",t[t.QUESTION=98]="QUESTION",t[t.COLON=99]="COLON",t[t.EQUALS=100]="EQUALS",t[t.PLUS_EQUALS=101]="PLUS_EQUALS",t[t.MINUS_EQUALS=102]="MINUS_EQUALS",t[t.ASTERISK_EQUALS=103]="ASTERISK_EQUALS",t[t.ASTERISK_ASTERISK_EQUALS=104]="ASTERISK_ASTERISK_EQUALS",t[t.SLASH_EQUALS=105]="SLASH_EQUALS",t[t.PERCENT_EQUALS=106]="PERCENT_EQUALS",t[t.LESSTHAN_LESSTHAN_EQUALS=107]="LESSTHAN_LESSTHAN_EQUALS",t[t.GREATERTHAN_GREATERTHAN_EQUALS=108]="GREATERTHAN_GREATERTHAN_EQUALS",t[t.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS=109]="GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS",t[t.AMPERSAND_EQUALS=110]="AMPERSAND_EQUALS",t[t.BAR_EQUALS=111]="BAR_EQUALS",t[t.CARET_EQUALS=112]="CARET_EQUALS",t[t.AT=113]="AT",t[t.IDENTIFIER=114]="IDENTIFIER",t[t.STRINGLITERAL=115]="STRINGLITERAL",t[t.INTEGERLITERAL=116]="INTEGERLITERAL",t[t.FLOATLITERAL=117]="FLOATLITERAL",t[t.INVALID=118]="INVALID",t[t.ENDOFFILE=119]="ENDOFFILE"}(e.Token||(e.Token={})),e.operatorTokenToString=function(t){switch(t){case 14:return"delete";case 29:return"in";case 30:return"instanceof";case 37:return"new";case 55:return"typeof";case 57:return"void";case 60:return"yield";case 68:return"...";case 70:return",";case 71:return"<";case 72:return">";case 73:return"<=";case 74:return">=";case 75:return"==";case 76:return"!=";case 77:return"===";case 78:return"!==";case 80:return"+";case 81:return"-";case 82:return"**";case 83:return"*";case 84:return"/";case 85:return"%";case 86:return"++";case 87:return"--";case 88:return"<<";case 89:return">>";case 90:return">>>";case 91:return"&";case 92:return"|";case 93:return"^";case 94:return"!";case 95:return"~";case 96:return"&&";case 97:return"||";case 100:return"=";case 101:return"+=";case 102:return"-=";case 103:return"*=";case 104:return"**=";case 105:return"/=";case 106:return"%=";case 107:return"<<=";case 108:return">>=";case 109:return">>>=";case 110:return"&=";case 111:return"|=";case 112:return"^=";default:return assert(!1),""}}}])})); \ No newline at end of file diff --git a/lib/transformer/dist/FuncVisitor.js b/lib/transformer/dist/FuncVisitor.js new file mode 100644 index 0000000000..c77dd408b7 --- /dev/null +++ b/lib/transformer/dist/FuncVisitor.js @@ -0,0 +1 @@ +!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define([],i):"object"==typeof exports?exports.transformer=i():t.transformer=i()}("undefined"!=typeof self?self:this,(function(){return function(t){var i={};function e(s){if(i[s])return i[s].exports;var n=i[s]={i:s,l:!1,exports:{}};return t[s].call(n.exports,n,n.exports,e),n.l=!0,n.exports}return e.m=t,e.c=i,e.d=function(t,i,s){e.o(t,i)||Object.defineProperty(t,i,{enumerable:!0,get:s})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,i){if(1&i&&(t=e(t)),8&i)return t;if(4&i&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(e.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&i&&"string"!=typeof t)for(var n in t)e.d(s,n,function(i){return t[i]}.bind(null,n));return s},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},e.p="",e(e.s=6)}([function(t,i,e){"use strict";Object.defineProperty(i,"__esModule",{value:!0}),function(t){t[t.SOURCE=0]="SOURCE",t[t.NAMEDTYPE=1]="NAMEDTYPE",t[t.FUNCTIONTYPE=2]="FUNCTIONTYPE",t[t.TYPENAME=3]="TYPENAME",t[t.TYPEPARAMETER=4]="TYPEPARAMETER",t[t.PARAMETER=5]="PARAMETER",t[t.IDENTIFIER=6]="IDENTIFIER",t[t.ASSERTION=7]="ASSERTION",t[t.BINARY=8]="BINARY",t[t.CALL=9]="CALL",t[t.CLASS=10]="CLASS",t[t.COMMA=11]="COMMA",t[t.ELEMENTACCESS=12]="ELEMENTACCESS",t[t.FALSE=13]="FALSE",t[t.FUNCTION=14]="FUNCTION",t[t.INSTANCEOF=15]="INSTANCEOF",t[t.LITERAL=16]="LITERAL",t[t.NEW=17]="NEW",t[t.NULL=18]="NULL",t[t.PARENTHESIZED=19]="PARENTHESIZED",t[t.PROPERTYACCESS=20]="PROPERTYACCESS",t[t.TERNARY=21]="TERNARY",t[t.SUPER=22]="SUPER",t[t.THIS=23]="THIS",t[t.TRUE=24]="TRUE",t[t.CONSTRUCTOR=25]="CONSTRUCTOR",t[t.UNARYPOSTFIX=26]="UNARYPOSTFIX",t[t.UNARYPREFIX=27]="UNARYPREFIX",t[t.BLOCK=28]="BLOCK",t[t.BREAK=29]="BREAK",t[t.CONTINUE=30]="CONTINUE",t[t.DO=31]="DO",t[t.EMPTY=32]="EMPTY",t[t.EXPORT=33]="EXPORT",t[t.EXPORTDEFAULT=34]="EXPORTDEFAULT",t[t.EXPORTIMPORT=35]="EXPORTIMPORT",t[t.EXPRESSION=36]="EXPRESSION",t[t.FOR=37]="FOR",t[t.IF=38]="IF",t[t.IMPORT=39]="IMPORT",t[t.RETURN=40]="RETURN",t[t.SWITCH=41]="SWITCH",t[t.THROW=42]="THROW",t[t.TRY=43]="TRY",t[t.VARIABLE=44]="VARIABLE",t[t.VOID=45]="VOID",t[t.WHILE=46]="WHILE",t[t.CLASSDECLARATION=47]="CLASSDECLARATION",t[t.ENUMDECLARATION=48]="ENUMDECLARATION",t[t.ENUMVALUEDECLARATION=49]="ENUMVALUEDECLARATION",t[t.FIELDDECLARATION=50]="FIELDDECLARATION",t[t.FUNCTIONDECLARATION=51]="FUNCTIONDECLARATION",t[t.IMPORTDECLARATION=52]="IMPORTDECLARATION",t[t.INDEXSIGNATUREDECLARATION=53]="INDEXSIGNATUREDECLARATION",t[t.INTERFACEDECLARATION=54]="INTERFACEDECLARATION",t[t.METHODDECLARATION=55]="METHODDECLARATION",t[t.NAMESPACEDECLARATION=56]="NAMESPACEDECLARATION",t[t.TYPEDECLARATION=57]="TYPEDECLARATION",t[t.VARIABLEDECLARATION=58]="VARIABLEDECLARATION",t[t.DECORATOR=59]="DECORATOR",t[t.EXPORTMEMBER=60]="EXPORTMEMBER",t[t.SWITCHCASE=61]="SWITCHCASE",t[t.COMMENT=62]="COMMENT"}(i.NodeKind||(i.NodeKind={})),function(t){t[t.DEFAULT=0]="DEFAULT",t[t.OPTIONAL=1]="OPTIONAL",t[t.REST=2]="REST"}(i.ParameterKind||(i.ParameterKind={})),function(t){t[t.CUSTOM=0]="CUSTOM",t[t.GLOBAL=1]="GLOBAL",t[t.OPERATOR=2]="OPERATOR",t[t.OPERATOR_BINARY=3]="OPERATOR_BINARY",t[t.OPERATOR_PREFIX=4]="OPERATOR_PREFIX",t[t.OPERATOR_POSTFIX=5]="OPERATOR_POSTFIX",t[t.UNMANAGED=6]="UNMANAGED",t[t.SEALED=7]="SEALED",t[t.INLINE=8]="INLINE",t[t.EXTERNAL=9]="EXTERNAL",t[t.BUILTIN=10]="BUILTIN",t[t.LAZY=11]="LAZY",t[t.UNSAFE=12]="UNSAFE"}(i.DecoratorKind||(i.DecoratorKind={})),i.DecoratorKind||(i.DecoratorKind={}),function(t){t[t.LINE=0]="LINE",t[t.TRIPLE=1]="TRIPLE",t[t.BLOCK=2]="BLOCK"}(i.CommentKind||(i.CommentKind={})),function(t){t[t.FLOAT=0]="FLOAT",t[t.INTEGER=1]="INTEGER",t[t.STRING=2]="STRING",t[t.REGEXP=3]="REGEXP",t[t.ARRAY=4]="ARRAY",t[t.OBJECT=5]="OBJECT"}(i.LiteralKind||(i.LiteralKind={})),function(t){t[t.PREFIX=0]="PREFIX",t[t.AS=1]="AS",t[t.NONNULL=2]="NONNULL"}(i.AssertionKind||(i.AssertionKind={})),function(t){t[t.USER=0]="USER",t[t.USER_ENTRY=1]="USER_ENTRY",t[t.LIBRARY=2]="LIBRARY",t[t.LIBRARY_ENTRY=3]="LIBRARY_ENTRY"}(i.SourceKind||(i.SourceKind={})),function(t){t[t.NONE=0]="NONE",t[t.ARROW_PARENTHESIZED=1]="ARROW_PARENTHESIZED",t[t.ARROW_SINGLE=2]="ARROW_SINGLE"}(i.ArrowKind||(i.ArrowKind={})),i.findDecorator=function(t,i){if(i)for(var e=0,s=i.length;e=t.length&&(t=void 0),{value:t&&t[s++],done:!t}}};throw new TypeError(i?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(i,"__esModule",{value:!0});var o=function(t){function i(){var i=null!==t&&t.apply(this,arguments)||this;return i.depth=0,i}return n(i,t),i.prototype._visit=function(t){switch(t.kind){case 0:this.visitSource(t);break;case 1:this.visitNamedTypeNode(t);break;case 2:this.visitFunctionTypeNode(t);break;case 3:this.visitTypeName(t);case 4:this.visitTypeParameter(t);break;case 13:case 18:case 22:case 23:case 24:case 25:case 6:this.visitIdentifierExpression(t);break;case 7:this.visitAssertionExpression(t);break;case 8:this.visitBinaryExpression(t);break;case 9:this.visitCallExpression(t);break;case 10:this.visitClassExpression(t);break;case 11:this.visitCommaExpression(t);break;case 12:this.visitElementAccessExpression(t);break;case 14:this.visitFunctionExpression(t);break;case 15:this.visitInstanceOfExpression(t);break;case 16:this.visitLiteralExpression(t);break;case 17:this.visitNewExpression(t);break;case 19:this.visitParenthesizedExpression(t);break;case 20:this.visitPropertyAccessExpression(t);break;case 21:this.visitTernaryExpression(t);break;case 26:this.visitUnaryPostfixExpression(t);break;case 27:this.visitUnaryPrefixExpression(t);break;case 28:this.visitBlockStatement(t);break;case 29:this.visitBreakStatement(t);break;case 30:this.visitContinueStatement(t);break;case 31:this.visitDoStatement(t);break;case 32:this.visitEmptyStatement(t);break;case 33:this.visitExportStatement(t);break;case 34:this.visitExportDefaultStatement(t);break;case 35:this.visitExportImportStatement(t);break;case 36:this.visitExpressionStatement(t);break;case 37:this.visitForStatement(t);break;case 38:this.visitIfStatement(t);break;case 39:this.visitImportStatement(t);break;case 40:this.visitReturnStatement(t);break;case 41:this.visitSwitchStatement(t);break;case 42:this.visitThrowStatement(t);break;case 43:this.visitTryStatement(t);break;case 44:this.visitVariableStatement(t);break;case 46:this.visitWhileStatement(t);break;case 47:this.visitClassDeclaration(t);break;case 48:this.visitEnumDeclaration(t);break;case 49:this.visitEnumValueDeclaration(t);break;case 50:this.visitFieldDeclaration(t);break;case 51:this.visitFunctionDeclaration(t);break;case 52:this.visitImportDeclaration(t);break;case 53:this.visitIndexSignatureDeclaration(t);break;case 54:this.visitInterfaceDeclaration(t);break;case 55:this.visitMethodDeclaration(t);break;case 56:this.visitNamespaceDeclaration(t);break;case 57:this.visitTypeDeclaration(t);break;case 58:this.visitVariableDeclaration(t);break;case 59:this.visitDecoratorNode(t);break;case 60:this.visitExportMember(t);break;case 5:this.visitParameter(t);break;case 61:this.visitSwitchCase(t);break;default:assert(!1)}},i.prototype.visitSource=function(t){var i,e;try{for(var s=r(t.statements),n=s.next();!n.done;n=s.next()){var o=n.value;this.depth++,this.visit(o),this.depth--}}catch(t){i={error:t}}finally{try{n&&!n.done&&(e=s.return)&&e.call(s)}finally{if(i)throw i.error}}},i.prototype.visitTypeNode=function(t){},i.prototype.visitTypeName=function(t){this.visit(t.identifier),t.next&&this.visit(t.next)},i.prototype.visitNamedTypeNode=function(t){this.visit(t.name),this.visit(t.typeArguments)},i.prototype.visitFunctionTypeNode=function(t){var i,e;try{for(var s=r(t.parameters),n=s.next();!n.done;n=s.next()){var o=n.value;this.visit(o)}}catch(t){i={error:t}}finally{try{n&&!n.done&&(e=s.return)&&e.call(s)}finally{if(i)throw i.error}}this.visit(t.returnType)},i.prototype.visitTypeParameter=function(t){this.visit(t.name),t.extendsType&&this.visit(t.extendsType),t.defaultType&&this.visit(t.defaultType)},i.prototype.visitIdentifierExpression=function(t){},i.prototype.visitArrayLiteralExpression=function(t){var i=this;t.elementExpressions.map((function(t){t&&i.visit(t)}))},i.prototype.visitObjectLiteralExpression=function(t){if(t.values&&t.names){assert(t.values.length==t.names.length);for(var i=0;i=t.length&&(t=void 0),{value:t&&t[s++],done:!t}}};throw new TypeError(i?"Object is not iterable.":"Symbol.iterator is not defined.")},n=this&&this.__read||function(t,i){var e="function"==typeof Symbol&&t[Symbol.iterator];if(!e)return t;var s,n,r=e.call(t),o=[];try{for(;(void 0===i||i-- >0)&&!(s=r.next()).done;)o.push(s.value)}catch(t){n={error:t}}finally{try{s&&!s.done&&(e=r.return)&&e.call(r)}finally{if(n)throw n.error}}return o};Object.defineProperty(i,"__esModule",{value:!0});var r=function(){function t(){}return t.prototype.visit=function(t){var i,e,r,o,a,p=this;if(null!=t)if(t instanceof Array)t.map((function(t){p.visit(t)}));else if(t instanceof Map)try{for(var c=s(t.entries()),E=c.next();!E.done;E=c.next()){var v=n(E.value,2),u=(v[0],v[1]);this.visit(u)}}catch(t){i={error:t}}finally{try{E&&!E.done&&(e=c.return)&&e.call(c)}finally{if(i)throw i.error}}else if(null!=(a=t)&&"function"==typeof a[Symbol.iterator])try{for(var h=s(t),l=h.next();!l.done;l=h.next()){u=l.value;this.visit(u)}}catch(t){r={error:t}}finally{try{l&&!l.done&&(o=h.return)&&o.call(h)}finally{if(r)throw r.error}}else this._visit(t)},t}();i.AbstractVisitor=r}])})); \ No newline at end of file diff --git a/lib/transformer/package.json b/lib/transformer/package.json new file mode 100644 index 0000000000..bd4d687691 --- /dev/null +++ b/lib/transformer/package.json @@ -0,0 +1,14 @@ +{ + "name": "as-transformer", + "description": "Tools for transforming the Abstract Syntax Tree (AST)", + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": { + "build": "npm run webpack", + "webpack": "../../node_modules/.bin/webpack --config=./webpack.config.js", + "build:dev": "npm run webpack -- --mode=development", + "test": "cd tests && npm run test" + }, + "author": "W", + "license": "MIT" +} diff --git a/lib/transformer/src/ASTBuilder.ts b/lib/transformer/src/ASTBuilder.ts new file mode 100644 index 0000000000..42b304ee2c --- /dev/null +++ b/lib/transformer/src/ASTBuilder.ts @@ -0,0 +1,1635 @@ +// tslint:disable: as-internal-case +import { indent , CharCode } from "./util"; +import { operatorTokenToString } from "./parsing"; + +import { + CommonFlags, + TypeNode, + Node, + NodeKind, + Source, + NamedTypeNode, + FunctionTypeNode, + TypeParameterNode, + IdentifierExpression, + CallExpression, + ClassExpression, + ElementAccessExpression, + FunctionExpression, + InstanceOfExpression, + LiteralExpression, + NewExpression, + ParenthesizedExpression, + PropertyAccessExpression, + TernaryExpression, + UnaryPostfixExpression, + UnaryPrefixExpression, + BlockStatement, + BreakStatement, + ContinueStatement, + DoStatement, + EmptyStatement, + ExportStatement, + ExportDefaultStatement, + ExportImportStatement, + ExpressionStatement, + ForStatement, + IfStatement, + ImportStatement, + ReturnStatement, + SwitchStatement, + ThrowStatement, + TryStatement, + VariableStatement, + WhileStatement, + ClassDeclaration, + EnumDeclaration, + EnumValueDeclaration, + FieldDeclaration, + FunctionDeclaration, + ImportDeclaration, + IndexSignatureDeclaration, + InterfaceDeclaration, + MethodDeclaration, + NamespaceDeclaration, + TypeDeclaration, + VariableDeclaration, + DecoratorNode, + ExportMember, + ParameterNode, + SwitchCase, + TypeName, + ArrayLiteralExpression, + Expression, + ObjectLiteralExpression, + AssertionKind, + LiteralKind, + FloatLiteralExpression, + StringLiteralExpression, + RegexpLiteralExpression, + UnaryExpression, + Statement, + ArrowKind, + ParameterKind, + DeclarationStatement, + AssertionExpression, + BinaryExpression, + CommaExpression, + IntegerLiteralExpression, + isTypeOmitted +} from "./ast"; + +// declare function i64_to_string(i: I64): string; +// import { i64_to_string } from "../../../src/glue/i64" + +/** An AST builder. */ +export class ASTBuilder { + /** Rebuilds the textual source from the specified AST, as far as possible. */ + static build(node: Node): string { + var builder = new ASTBuilder(); + builder.visitNode(node); + return builder.finish(); + } + + private sb: string[] = []; + private indentLevel: i32 = 0; + + visitNode(node: Node): void { + switch (node.kind) { + case NodeKind.SOURCE: { + this.visitSource(node); + break; + } + + // types + + case NodeKind.NAMEDTYPE: { + this.visitNamedTypeNode(node); + break; + } + case NodeKind.FUNCTIONTYPE: { + this.visitFunctionTypeNode(node); + break; + } + case NodeKind.TYPEPARAMETER: { + this.visitTypeParameter(node); + break; + } + + // expressions + + case NodeKind.FALSE: + case NodeKind.NULL: + case NodeKind.SUPER: + case NodeKind.THIS: + case NodeKind.TRUE: + case NodeKind.CONSTRUCTOR: + case NodeKind.IDENTIFIER: { + this.visitIdentifierExpression(node); + break; + } + case NodeKind.ASSERTION: { + this.visitAssertionExpression(node); + break; + } + case NodeKind.BINARY: { + this.visitBinaryExpression(node); + break; + } + case NodeKind.CALL: { + this.visitCallExpression(node); + break; + } + case NodeKind.CLASS: { + this.visitClassExpression(node); + break; + } + case NodeKind.COMMA: { + this.visitCommaExpression(node); + break; + } + case NodeKind.ELEMENTACCESS: { + this.visitElementAccessExpression(node); + break; + } + case NodeKind.FUNCTION: { + this.visitFunctionExpression(node); + break; + } + case NodeKind.INSTANCEOF: { + this.visitInstanceOfExpression(node); + break; + } + case NodeKind.LITERAL: { + this.visitLiteralExpression(node); + break; + } + case NodeKind.NEW: { + this.visitNewExpression(node); + break; + } + case NodeKind.PARENTHESIZED: { + this.visitParenthesizedExpression(node); + break; + } + case NodeKind.PROPERTYACCESS: { + this.visitPropertyAccessExpression(node); + break; + } + case NodeKind.TERNARY: { + this.visitTernaryExpression(node); + break; + } + case NodeKind.UNARYPOSTFIX: { + this.visitUnaryPostfixExpression(node); + break; + } + case NodeKind.UNARYPREFIX: { + this.visitUnaryPrefixExpression(node); + break; + } + + // statements + + case NodeKind.BLOCK: { + this.visitBlockStatement(node); + break; + } + case NodeKind.BREAK: { + this.visitBreakStatement(node); + break; + } + case NodeKind.CONTINUE: { + this.visitContinueStatement(node); + break; + } + case NodeKind.DO: { + this.visitDoStatement(node); + break; + } + case NodeKind.EMPTY: { + this.visitEmptyStatement(node); + break; + } + case NodeKind.EXPORT: { + this.visitExportStatement(node); + break; + } + case NodeKind.EXPORTDEFAULT: { + this.visitExportDefaultStatement(node); + break; + } + case NodeKind.EXPORTIMPORT: { + this.visitExportImportStatement(node); + break; + } + case NodeKind.EXPRESSION: { + this.visitExpressionStatement(node); + break; + } + case NodeKind.FOR: { + this.visitForStatement(node); + break; + } + case NodeKind.IF: { + this.visitIfStatement(node); + break; + } + case NodeKind.IMPORT: { + this.visitImportStatement(node); + break; + } + case NodeKind.RETURN: { + this.visitReturnStatement(node); + break; + } + case NodeKind.SWITCH: { + this.visitSwitchStatement(node); + break; + } + case NodeKind.THROW: { + this.visitThrowStatement(node); + break; + } + case NodeKind.TRY: { + this.visitTryStatement(node); + break; + } + case NodeKind.VARIABLE: { + this.visitVariableStatement(node); + break; + } + case NodeKind.WHILE: { + this.visitWhileStatement(node); + break; + } + + // declaration statements + + case NodeKind.CLASSDECLARATION: { + this.visitClassDeclaration(node); + break; + } + case NodeKind.ENUMDECLARATION: { + this.visitEnumDeclaration(node); + break; + } + case NodeKind.ENUMVALUEDECLARATION: { + this.visitEnumValueDeclaration(node); + break; + } + case NodeKind.FIELDDECLARATION: { + this.visitFieldDeclaration(node); + break; + } + case NodeKind.FUNCTIONDECLARATION: { + this.visitFunctionDeclaration(node); + break; + } + case NodeKind.IMPORTDECLARATION: { + this.visitImportDeclaration(node); + break; + } + case NodeKind.INDEXSIGNATUREDECLARATION: { + this.visitIndexSignatureDeclaration(node); + break; + } + case NodeKind.INTERFACEDECLARATION: { + this.visitInterfaceDeclaration(node); + break; + } + case NodeKind.METHODDECLARATION: { + this.visitMethodDeclaration(node); + break; + } + case NodeKind.NAMESPACEDECLARATION: { + this.visitNamespaceDeclaration(node); + break; + } + case NodeKind.TYPEDECLARATION: { + this.visitTypeDeclaration(node); + break; + } + case NodeKind.VARIABLEDECLARATION: { + this.visitVariableDeclaration(node); + break; + } + + // other + + case NodeKind.DECORATOR: { + this.serializeDecorator(node); + break; + } + case NodeKind.EXPORTMEMBER: { + this.visitExportMember(node); + break; + } + case NodeKind.PARAMETER: { + this.serializeParameter(node); + break; + } + case NodeKind.SWITCHCASE: { + this.visitSwitchCase(node); + break; + } + default: + assert(false); + } + } + + visitSource(source: Source): void { + var statements = source.statements; + for (let i = 0, k = statements.length; i < k; ++i) { + this.visitNodeAndTerminate(statements[i]); + } + } + + // types + + visitTypeNode(node: TypeNode): void { + switch (node.kind) { + case NodeKind.NAMEDTYPE: { + this.visitNamedTypeNode(node); + break; + } + case NodeKind.FUNCTIONTYPE: { + this.visitFunctionTypeNode(node); + break; + } + default: + assert(false); + } + } + + visitTypeName(node: TypeName): void { + this.visitIdentifierExpression(node.identifier); + var sb = this.sb; + var current = node.next; + while (current) { + sb.push("."); + this.visitIdentifierExpression(current.identifier); + current = current.next; + } + } + + visitNamedTypeNode(node: NamedTypeNode): void { + this.visitTypeName(node.name); + var typeArguments = node.typeArguments; + if (typeArguments) { + let numTypeArguments = typeArguments.length; + let sb = this.sb; + if (numTypeArguments) { + sb.push("<"); + this.visitTypeNode(typeArguments[0]); + for (let i = 1; i < numTypeArguments; ++i) { + sb.push(", "); + this.visitTypeNode(typeArguments[i]); + } + sb.push(">"); + } + if (node.isNullable) sb.push(" | null"); + } + } + + visitFunctionTypeNode(node: FunctionTypeNode): void { + var isNullable = node.isNullable; + var sb = this.sb; + sb.push(isNullable ? "((" : "("); + var explicitThisType = node.explicitThisType; + if (explicitThisType) { + sb.push("this: "); + this.visitTypeNode(explicitThisType); + } + var parameters = node.parameters; + var numParameters = parameters.length; + if (numParameters) { + if (explicitThisType) sb.push(", "); + this.serializeParameter(parameters[0]); + for (let i = 1; i < numParameters; ++i) { + sb.push(", "); + this.serializeParameter(parameters[i]); + } + } + var returnType = node.returnType; + if (returnType) { + sb.push(") => "); + this.visitTypeNode(returnType); + } else { + sb.push(") => void"); + } + if (isNullable) sb.push(") | null"); + } + + visitTypeParameter(node: TypeParameterNode): void { + this.visitIdentifierExpression(node.name); + var extendsType = node.extendsType; + if (extendsType) { + this.sb.push(" extends "); + this.visitTypeNode(extendsType); + } + var defaultType = node.defaultType; + if (defaultType) { + this.sb.push("="); + this.visitTypeNode(defaultType); + } + } + + // expressions + + visitIdentifierExpression(node: IdentifierExpression): void { + if (node.isQuoted) this.visitStringLiteral(node.text); + else this.sb.push(node.text); + } + + visitArrayLiteralExpression(node: ArrayLiteralExpression): void { + var sb = this.sb; + sb.push("["); + var elements = node.elementExpressions; + var numElements = elements.length; + if (numElements) { + if (elements[0]) this.visitNode(elements[0]); + for (let i = 1; i < numElements; ++i) { + sb.push(", "); + if (elements[i]) this.visitNode(elements[i]); + } + } + sb.push("]"); + } + + visitObjectLiteralExpression(node: ObjectLiteralExpression): void { + var sb = this.sb; + var names = node.names; + var values = node.values; + var numElements = names.length; + assert(numElements == values.length); + if (numElements) { + sb.push("{\n"); + indent(sb, ++this.indentLevel); + this.visitNode(names[0]); + sb.push(": "); + this.visitNode(values[0]); + for (let i = 1; i < numElements; ++i) { + sb.push(",\n"); + indent(sb, this.indentLevel); + let name = names[i]; + let value = values[i]; + if (name === value) { + this.visitNode(name); + } else { + this.visitNode(name); + sb.push(": "); + this.visitNode(value); + } + } + sb.push("\n"); + indent(sb, --this.indentLevel); + sb.push("}"); + } else { + sb.push("{}"); + } + } + + visitAssertionExpression(node: AssertionExpression): void { + var sb = this.sb; + switch (node.assertionKind) { + case AssertionKind.PREFIX: { + sb.push("<"); + this.visitTypeNode(assert(node.toType)); + sb.push(">"); + this.visitNode(node.expression); + break; + } + case AssertionKind.AS: { + this.visitNode(node.expression); + sb.push(" as "); + this.visitTypeNode(assert(node.toType)); + break; + } + case AssertionKind.NONNULL: { + this.visitNode(node.expression); + sb.push("!"); + break; + } + default: + assert(false); + } + } + + visitBinaryExpression(node: BinaryExpression): void { + var sb = this.sb; + this.visitNode(node.left); + sb.push(" "); + sb.push(operatorTokenToString(node.operator)); + sb.push(" "); + this.visitNode(node.right); + } + + visitCallExpression(node: CallExpression): void { + var sb = this.sb; + this.visitNode(node.expression); + var typeArguments = node.typeArguments; + if (typeArguments) { + let numTypeArguments = typeArguments.length; + if (numTypeArguments) { + sb.push("<"); + this.visitTypeNode(typeArguments[0]); + for (let i = 1; i < numTypeArguments; ++i) { + sb.push(", "); + this.visitTypeNode(typeArguments[i]); + } + sb.push(">("); + } + } else { + sb.push("("); + } + var args = node.arguments; + var numArgs = args.length; + if (numArgs) { + this.visitNode(args[0]); + for (let i = 1; i < numArgs; ++i) { + sb.push(", "); + this.visitNode(args[i]); + } + } + sb.push(")"); + } + + visitClassExpression(node: ClassExpression): void { + var declaration = node.declaration; + this.visitClassDeclaration(declaration); + } + + visitCommaExpression(node: CommaExpression): void { + var expressions = node.expressions; + var numExpressions = assert(expressions.length); + this.visitNode(expressions[0]); + var sb = this.sb; + for (let i = 1; i < numExpressions; ++i) { + sb.push(","); + this.visitNode(expressions[i]); + } + } + + visitElementAccessExpression(node: ElementAccessExpression): void { + var sb = this.sb; + this.visitNode(node.expression); + sb.push("["); + this.visitNode(node.elementExpression); + sb.push("]"); + } + + visitFunctionExpression(node: FunctionExpression): void { + var declaration = node.declaration; + if (!declaration.arrowKind) { + if (declaration.name.text.length) { + this.sb.push("function "); + } else { + this.sb.push("function"); + } + } else { + assert(declaration.name.text.length == 0); + } + this.visitFunctionCommon(declaration); + } + + visitLiteralExpression(node: LiteralExpression): void { + switch (node.literalKind) { + case LiteralKind.FLOAT: { + this.visitFloatLiteralExpression(node); + break; + } + case LiteralKind.INTEGER: { + this.visitIntegerLiteralExpression(node); + break; + } + case LiteralKind.STRING: { + this.visitStringLiteralExpression(node); + break; + } + case LiteralKind.REGEXP: { + this.visitRegexpLiteralExpression(node); + break; + } + case LiteralKind.ARRAY: { + this.visitArrayLiteralExpression(node); + break; + } + case LiteralKind.OBJECT: { + this.visitObjectLiteralExpression(node); + break; + } + default: { + assert(false); + break; + } + } + } + + visitFloatLiteralExpression(node: FloatLiteralExpression): void { + this.sb.push(node.value.toString(10)); + } + + visitInstanceOfExpression(node: InstanceOfExpression): void { + this.visitNode(node.expression); + this.sb.push(" instanceof "); + this.visitTypeNode(node.isType); + } + + visitIntegerLiteralExpression(node: IntegerLiteralExpression): void { + this.sb.push(i64_to_string(node.value)); + } + + visitStringLiteral(str: string, singleQuoted: bool = false): void { + var sb = this.sb; + var off = 0; + var quote = singleQuoted ? "'" : "\""; + sb.push(quote); + var i = 0; + for (let k = str.length; i < k; ) { + switch (str.charCodeAt(i)) { + case CharCode.NULL: { + if (i > off) sb.push(str.substring(off, (off = i + 1))); + sb.push("\\0"); + off = ++i; + break; + } + case CharCode.BACKSPACE: { + if (i > off) sb.push(str.substring(off, i)); + off = ++i; + sb.push("\\b"); + break; + } + case CharCode.TAB: { + if (i > off) sb.push(str.substring(off, i)); + off = ++i; + sb.push("\\t"); + break; + } + case CharCode.LINEFEED: { + if (i > off) sb.push(str.substring(off, i)); + off = ++i; + sb.push("\\n"); + break; + } + case CharCode.VERTICALTAB: { + if (i > off) sb.push(str.substring(off, i)); + off = ++i; + sb.push("\\v"); + break; + } + case CharCode.FORMFEED: { + if (i > off) sb.push(str.substring(off, i)); + off = ++i; + sb.push("\\f"); + break; + } + case CharCode.CARRIAGERETURN: { + if (i > off) sb.push(str.substring(off, i)); + sb.push("\\r"); + off = ++i; + break; + } + case CharCode.DOUBLEQUOTE: { + if (!singleQuoted) { + if (i > off) sb.push(str.substring(off, i)); + sb.push("\\\""); + off = ++i; + } else { + ++i; + } + break; + } + case CharCode.SINGLEQUOTE: { + if (singleQuoted) { + if (i > off) sb.push(str.substring(off, i)); + sb.push("\\'"); + off = ++i; + } else { + ++i; + } + break; + } + case CharCode.BACKSLASH: { + if (i > off) sb.push(str.substring(off, i)); + sb.push("\\\\"); + off = ++i; + break; + } + default: { + ++i; + break; + } + } + } + if (i > off) sb.push(str.substring(off, i)); + sb.push(quote); + } + + visitStringLiteralExpression(node: StringLiteralExpression): void { + this.visitStringLiteral(node.value); + } + + visitRegexpLiteralExpression(node: RegexpLiteralExpression): void { + var sb = this.sb; + sb.push("/"); + sb.push(node.pattern); + sb.push("/"); + sb.push(node.patternFlags); + } + + visitNewExpression(node: NewExpression): void { + this.sb.push("new "); + this.visitCallExpression(node); + } + + visitParenthesizedExpression(node: ParenthesizedExpression): void { + var sb = this.sb; + sb.push("("); + this.visitNode(node.expression); + sb.push(")"); + } + + visitPropertyAccessExpression(node: PropertyAccessExpression): void { + this.visitNode(node.expression); + this.sb.push("."); + this.visitIdentifierExpression(node.property); + } + + visitTernaryExpression(node: TernaryExpression): void { + var sb = this.sb; + this.visitNode(node.condition); + sb.push(" ? "); + this.visitNode(node.ifThen); + sb.push(" : "); + this.visitNode(node.ifElse); + } + + visitUnaryExpression(node: UnaryExpression): void { + switch (node.kind) { + case NodeKind.UNARYPOSTFIX: { + this.visitUnaryPostfixExpression(node); + break; + } + case NodeKind.UNARYPREFIX: { + this.visitUnaryPrefixExpression(node); + break; + } + default: + assert(false); + } + } + + visitUnaryPostfixExpression(node: UnaryPostfixExpression): void { + this.visitNode(node.operand); + this.sb.push(operatorTokenToString(node.operator)); + } + + visitUnaryPrefixExpression(node: UnaryPrefixExpression): void { + this.sb.push(operatorTokenToString(node.operator)); + this.visitNode(node.operand); + } + + // statements + + visitNodeAndTerminate(statement: Statement): void { + this.visitNode(statement); + var sb = this.sb; + if ( + !sb.length || // leading EmptyStatement + statement.kind == NodeKind.VARIABLE || // potentially assigns a FunctionExpression + statement.kind == NodeKind.EXPRESSION // potentially assigns a FunctionExpression + ) { + sb.push(";\n"); + } else { + let last = sb[sb.length - 1]; + let lastCharPos = last.length - 1; + if ( + lastCharPos >= 0 && + (last.charCodeAt(lastCharPos) == CharCode.CLOSEBRACE || + last.charCodeAt(lastCharPos) == CharCode.SEMICOLON) + ) { + sb.push("\n"); + } else { + sb.push(";\n"); + } + } + } + + visitBlockStatement(node: BlockStatement): void { + var sb = this.sb; + var statements = node.statements; + var numStatements = statements.length; + if (numStatements) { + sb.push("{\n"); + let indentLevel = ++this.indentLevel; + for (let i = 0; i < numStatements; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(statements[i]); + } + indent(sb, --this.indentLevel); + sb.push("}"); + } else { + sb.push("{}"); + } + } + + visitBreakStatement(node: BreakStatement): void { + var label = node.label; + if (label) { + this.sb.push("break "); + this.visitIdentifierExpression(label); + } else { + this.sb.push("break"); + } + } + + visitContinueStatement(node: ContinueStatement): void { + var label = node.label; + if (label) { + this.sb.push("continue "); + this.visitIdentifierExpression(label); + } else { + this.sb.push("continue"); + } + } + + visitClassDeclaration(node: ClassDeclaration, isDefault: bool = false): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + var sb = this.sb; + if (isDefault) { + sb.push("export default "); + } else { + this.serializeExternalModifiers(node); + } + if (node.is(CommonFlags.ABSTRACT)) sb.push("abstract "); + if (node.name.text.length) { + sb.push("class "); + this.visitIdentifierExpression(node.name); + } else { + sb.push("class"); + } + var typeParameters = node.typeParameters; + if (typeParameters && typeParameters.length) { + sb.push("<"); + this.visitTypeParameter(typeParameters[0]); + for (let i = 1, k = typeParameters.length; i < k; ++i) { + sb.push(", "); + this.visitTypeParameter(typeParameters[i]); + } + sb.push(">"); + } + var extendsType = node.extendsType; + if (extendsType) { + sb.push(" extends "); + this.visitTypeNode(extendsType); + } + var implementsTypes = node.implementsTypes; + if (implementsTypes) { + let numImplementsTypes = implementsTypes.length; + if (numImplementsTypes) { + sb.push(" implements "); + this.visitTypeNode(implementsTypes[0]); + for (let i = 1; i < numImplementsTypes; ++i) { + sb.push(", "); + this.visitTypeNode(implementsTypes[i]); + } + } + } + var members = node.members; + var numMembers = members.length; + if (numMembers) { + sb.push(" {\n"); + let indentLevel = ++this.indentLevel; + for (let i = 0, k = members.length; i < k; ++i) { + let member = members[i]; + if ( + member.kind != NodeKind.FIELDDECLARATION || + (member).parameterIndex < 0 + ) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(member); + } + } + indent(sb, --this.indentLevel); + sb.push("}"); + } else { + sb.push(" {}"); + } + } + + visitDoStatement(node: DoStatement): void { + var sb = this.sb; + sb.push("do "); + this.visitNode(node.statement); + if (node.statement.kind == NodeKind.BLOCK) { + sb.push(" while ("); + } else { + sb.push(";\n"); + indent(sb, this.indentLevel); + sb.push("while ("); + } + this.visitNode(node.condition); + sb.push(")"); + } + + visitEmptyStatement(node: EmptyStatement): void {} + + visitEnumDeclaration(node: EnumDeclaration, isDefault: bool = false): void { + var sb = this.sb; + if (isDefault) { + sb.push("export default "); + } else { + this.serializeExternalModifiers(node); + } + if (node.is(CommonFlags.CONST)) sb.push("const "); + sb.push("enum "); + this.visitIdentifierExpression(node.name); + var values = node.values; + var numValues = values.length; + if (numValues) { + sb.push(" {\n"); + let indentLevel = ++this.indentLevel; + indent(sb, indentLevel); + this.visitEnumValueDeclaration(node.values[0]); + for (let i = 1; i < numValues; ++i) { + sb.push(",\n"); + indent(sb, indentLevel); + this.visitEnumValueDeclaration(node.values[i]); + } + sb.push("\n"); + indent(sb, --this.indentLevel); + sb.push("}"); + } else { + sb.push(" {}"); + } + } + + visitEnumValueDeclaration(node: EnumValueDeclaration): void { + this.visitIdentifierExpression(node.name); + if (node.value) { + this.sb.push(" = "); + this.visitNode(node.value); + } + } + + visitExportImportStatement(node: ExportImportStatement): void { + var sb = this.sb; + sb.push("export import "); + this.visitIdentifierExpression(node.externalName); + sb.push(" = "); + this.visitIdentifierExpression(node.name); + } + + visitExportMember(node: ExportMember): void { + this.visitIdentifierExpression(node.localName); + if (node.exportedName.text != node.localName.text) { + this.sb.push(" as "); + this.visitIdentifierExpression(node.exportedName); + } + } + + visitExportStatement(node: ExportStatement): void { + var sb = this.sb; + if (node.isDeclare) { + sb.push("declare "); + } + var members = node.members; + if (members && members.length) { + let numMembers = members.length; + sb.push("export {\n"); + let indentLevel = ++this.indentLevel; + indent(sb, indentLevel); + this.visitExportMember(members[0]); + for (let i = 1; i < numMembers; ++i) { + sb.push(",\n"); + indent(sb, indentLevel); + this.visitExportMember(members[i]); + } + --this.indentLevel; + sb.push("\n}"); + } else { + sb.push("export {}"); + } + var path = node.path; + if (path) { + sb.push(" from "); + this.visitStringLiteralExpression(path); + } + sb.push(";"); + } + + visitExportDefaultStatement(node: ExportDefaultStatement): void { + var declaration = node.declaration; + switch (declaration.kind) { + case NodeKind.ENUMDECLARATION: { + this.visitEnumDeclaration(declaration, true); + break; + } + case NodeKind.FUNCTIONDECLARATION: { + this.visitFunctionDeclaration(declaration, true); + break; + } + case NodeKind.CLASSDECLARATION: { + this.visitClassDeclaration(declaration, true); + break; + } + case NodeKind.INTERFACEDECLARATION: { + this.visitInterfaceDeclaration(declaration, true); + break; + } + case NodeKind.NAMESPACEDECLARATION: { + this.visitNamespaceDeclaration(declaration, true); + break; + } + default: + assert(false); + } + } + + visitExpressionStatement(node: ExpressionStatement): void { + this.visitNode(node.expression); + } + + visitFieldDeclaration(node: FieldDeclaration): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + this.serializeAccessModifiers(node); + this.visitIdentifierExpression(node.name); + var sb = this.sb; + if (node.flags & CommonFlags.DEFINITE_ASSIGNMENT) { + sb.push("!"); + } + var type = node.type; + if (type) { + sb.push(": "); + this.visitTypeNode(type); + } + var initializer = node.initializer; + if (initializer) { + sb.push(" = "); + this.visitNode(initializer); + } + } + + visitForStatement(node: ForStatement): void { + var sb = this.sb; + sb.push("for ("); + var initializer = node.initializer; + if (initializer) { + this.visitNode(initializer); + } + var condition = node.condition; + if (condition) { + sb.push("; "); + this.visitNode(condition); + } else { + sb.push(";"); + } + var incrementor = node.incrementor; + if (incrementor) { + sb.push("; "); + this.visitNode(incrementor); + } else { + sb.push(";"); + } + sb.push(") "); + this.visitNode(node.statement); + } + + visitFunctionDeclaration( + node: FunctionDeclaration, + isDefault: bool = false + ): void { + var sb = this.sb; + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + if (isDefault) { + sb.push("export default "); + } else { + this.serializeExternalModifiers(node); + this.serializeAccessModifiers(node); + } + if (node.name.text.length) { + sb.push("function "); + } else { + sb.push("function"); + } + this.visitFunctionCommon(node); + } + + visitFunctionCommon(node: FunctionDeclaration): void { + var sb = this.sb; + this.visitIdentifierExpression(node.name); + var signature = node.signature; + var typeParameters = node.typeParameters; + if (typeParameters) { + let numTypeParameters = typeParameters.length; + if (numTypeParameters) { + sb.push("<"); + this.visitTypeParameter(typeParameters[0]); + for (let i = 1; i < numTypeParameters; ++i) { + sb.push(", "); + this.visitTypeParameter(typeParameters[i]); + } + sb.push(">"); + } + } + if (node.arrowKind == ArrowKind.ARROW_SINGLE) { + let parameters = signature.parameters; + assert(parameters.length == 1); + assert(!signature.explicitThisType); + this.serializeParameter(parameters[0]); + } else { + sb.push("("); + let parameters = signature.parameters; + let numParameters = parameters.length; + let explicitThisType = signature.explicitThisType; + if (explicitThisType) { + sb.push("this: "); + this.visitTypeNode(explicitThisType); + } + if (numParameters) { + if (explicitThisType) sb.push(", "); + this.serializeParameter(parameters[0]); + for (let i = 1; i < numParameters; ++i) { + sb.push(", "); + this.serializeParameter(parameters[i]); + } + } + } + var body = node.body; + var returnType = signature.returnType; + if (node.arrowKind) { + if (body) { + if (node.arrowKind == ArrowKind.ARROW_SINGLE) { + assert(isTypeOmitted(returnType)); + } else { + if (isTypeOmitted(returnType)) { + sb.push(")"); + } else { + sb.push("): "); + this.visitTypeNode(returnType); + } + } + sb.push(" => "); + this.visitNode(body); + } else { + assert(!isTypeOmitted(returnType)); + sb.push(" => "); + this.visitTypeNode(returnType); + } + } else { + if ( + !isTypeOmitted(returnType) && + !node.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.SET) + ) { + sb.push("): "); + this.visitTypeNode(returnType); + } else { + sb.push(")"); + } + if (body) { + sb.push(" "); + this.visitNode(body); + } + } + } + + visitIfStatement(node: IfStatement): void { + var sb = this.sb; + sb.push("if ("); + this.visitNode(node.condition); + sb.push(") "); + var ifTrue = node.ifTrue; + this.visitNode(ifTrue); + if (ifTrue.kind != NodeKind.BLOCK) { + sb.push(";\n"); + } + var ifFalse = node.ifFalse; + if (ifFalse) { + if (ifTrue.kind == NodeKind.BLOCK) { + sb.push(" else "); + } else { + sb.push("else "); + } + this.visitNode(ifFalse); + } + } + + visitImportDeclaration(node: ImportDeclaration): void { + var externalName = node.foreignName; + var name = node.name; + this.visitIdentifierExpression(externalName); + if (externalName.text != name.text) { + this.sb.push(" as "); + this.visitIdentifierExpression(name); + } + } + + visitImportStatement(node: ImportStatement): void { + var sb = this.sb; + sb.push("import "); + var declarations = node.declarations; + var namespaceName = node.namespaceName; + if (declarations) { + let numDeclarations = declarations.length; + if (numDeclarations) { + sb.push("{\n"); + let indentLevel = ++this.indentLevel; + indent(sb, indentLevel); + this.visitImportDeclaration(declarations[0]); + for (let i = 1; i < numDeclarations; ++i) { + sb.push(",\n"); + indent(sb, indentLevel); + this.visitImportDeclaration(declarations[i]); + } + --this.indentLevel; + sb.push("\n} from "); + } else { + sb.push("{} from "); + } + } else if (namespaceName) { + sb.push("* as "); + this.visitIdentifierExpression(namespaceName); + sb.push(" from "); + } + this.visitStringLiteralExpression(node.path); + } + + visitIndexSignatureDeclaration(node: IndexSignatureDeclaration): void { + var sb = this.sb; + sb.push("[key: "); + this.visitTypeNode(node.keyType); + sb.push("]: "); + this.visitTypeNode(node.valueType); + } + + visitInterfaceDeclaration( + node: InterfaceDeclaration, + isDefault: bool = false + ): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + var sb = this.sb; + if (isDefault) { + sb.push("export default "); + } else { + this.serializeExternalModifiers(node); + } + sb.push("interface "); + this.visitIdentifierExpression(node.name); + var typeParameters = node.typeParameters; + if (typeParameters && typeParameters.length) { + sb.push("<"); + this.visitTypeParameter(typeParameters[0]); + for (let i = 1, k = typeParameters.length; i < k; ++i) { + sb.push(", "); + this.visitTypeParameter(typeParameters[i]); + } + sb.push(">"); + } + var extendsType = node.extendsType; + if (extendsType) { + sb.push(" extends "); + this.visitTypeNode(extendsType); + } + // must not have implementsTypes + sb.push(" {\n"); + var indentLevel = ++this.indentLevel; + var members = node.members; + for (let i = 0, k = members.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(members[i]); + } + --this.indentLevel; + sb.push("}"); + } + + visitMethodDeclaration(node: MethodDeclaration): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + this.serializeAccessModifiers(node); + if (node.is(CommonFlags.GET)) { + this.sb.push("get "); + } else if (node.is(CommonFlags.SET)) { + this.sb.push("set "); + } + this.visitFunctionCommon(node); + } + + visitNamespaceDeclaration( + node: NamespaceDeclaration, + isDefault: bool = false + ): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + var sb = this.sb; + if (isDefault) { + sb.push("export default "); + } else { + this.serializeExternalModifiers(node); + } + sb.push("namespace "); + this.visitIdentifierExpression(node.name); + var members = node.members; + var numMembers = members.length; + if (numMembers) { + sb.push(" {\n"); + let indentLevel = ++this.indentLevel; + for (let i = 0, k = members.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(members[i]); + } + indent(sb, --this.indentLevel); + sb.push("}"); + } else { + sb.push(" {}"); + } + } + + visitReturnStatement(node: ReturnStatement): void { + var value = node.value; + if (value) { + this.sb.push("return "); + this.visitNode(value); + } else { + this.sb.push("return"); + } + } + + visitSwitchCase(node: SwitchCase): void { + var sb = this.sb; + var label = node.label; + if (label) { + sb.push("case "); + this.visitNode(label); + sb.push(":\n"); + } else { + sb.push("default:\n"); + } + var statements = node.statements; + var numStatements = statements.length; + if (numStatements) { + let indentLevel = ++this.indentLevel; + indent(sb, indentLevel); + this.visitNodeAndTerminate(statements[0]); + for (let i = 1; i < numStatements; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(statements[i]); + } + --this.indentLevel; + } + } + + visitSwitchStatement(node: SwitchStatement): void { + var sb = this.sb; + sb.push("switch ("); + this.visitNode(node.condition); + sb.push(") {\n"); + var indentLevel = ++this.indentLevel; + var cases = node.cases; + for (let i = 0, k = cases.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitSwitchCase(cases[i]); + sb.push("\n"); + } + --this.indentLevel; + sb.push("}"); + } + + visitThrowStatement(node: ThrowStatement): void { + this.sb.push("throw "); + this.visitNode(node.value); + } + + visitTryStatement(node: TryStatement): void { + var sb = this.sb; + sb.push("try {\n"); + var indentLevel = ++this.indentLevel; + var statements = node.statements; + for (let i = 0, k = statements.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(statements[i]); + } + var catchVariable = node.catchVariable; + if (catchVariable) { + indent(sb, indentLevel - 1); + sb.push("} catch ("); + this.visitIdentifierExpression(catchVariable); + sb.push(") {\n"); + let catchStatements = node.catchStatements; + if (catchStatements) { + for (let i = 0, k = catchStatements.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(catchStatements[i]); + } + } + } + var finallyStatements = node.finallyStatements; + if (finallyStatements) { + indent(sb, indentLevel - 1); + sb.push("} finally {\n"); + for (let i = 0, k = finallyStatements.length; i < k; ++i) { + indent(sb, indentLevel); + this.visitNodeAndTerminate(finallyStatements[i]); + } + } + indent(sb, indentLevel - 1); + sb.push("}"); + } + + visitTypeDeclaration(node: TypeDeclaration): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + var sb = this.sb; + this.serializeExternalModifiers(node); + sb.push("type "); + this.visitIdentifierExpression(node.name); + var typeParameters = node.typeParameters; + if (typeParameters) { + let numTypeParameters = typeParameters.length; + if (numTypeParameters) { + sb.push("<"); + for (let i = 0; i < numTypeParameters; ++i) { + this.visitTypeParameter(typeParameters[i]); + } + sb.push(">"); + } + } + sb.push(" = "); + this.visitTypeNode(node.type); + } + + visitVariableDeclaration(node: VariableDeclaration): void { + this.visitIdentifierExpression(node.name); + var type = node.type; + var sb = this.sb; + if (node.flags & CommonFlags.DEFINITE_ASSIGNMENT) { + sb.push("!"); + } + if (type) { + sb.push(": "); + this.visitTypeNode(type); + } + var initializer = node.initializer; + if (initializer) { + sb.push(" = "); + this.visitNode(initializer); + } + } + + visitVariableStatement(node: VariableStatement): void { + var decorators = node.decorators; + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + this.serializeDecorator(decorators[i]); + } + } + var sb = this.sb; + var declarations = node.declarations; + var numDeclarations = assert(declarations.length); + var firstDeclaration = declarations[0]; + this.serializeExternalModifiers(firstDeclaration); + sb.push( + firstDeclaration.is(CommonFlags.CONST) + ? "const " + : firstDeclaration.is(CommonFlags.LET) + ? "let " + : "var " + ); + this.visitVariableDeclaration(node.declarations[0]); + for (let i = 1; i < numDeclarations; ++i) { + sb.push(", "); + this.visitVariableDeclaration(node.declarations[i]); + } + } + + visitWhileStatement(node: WhileStatement): void { + var sb = this.sb; + sb.push("while ("); + this.visitNode(node.condition); + var statement = node.statement; + if (statement.kind == NodeKind.EMPTY) { + sb.push(")"); + } else { + sb.push(") "); + this.visitNode(node.statement); + } + } + + // other + + serializeDecorator(node: DecoratorNode): void { + var sb = this.sb; + sb.push("@"); + this.visitNode(node.name); + var args = node.arguments; + if (args) { + sb.push("("); + let numArgs = args.length; + if (numArgs) { + this.visitNode(args[0]); + for (let i = 1; i < numArgs; ++i) { + sb.push(", "); + this.visitNode(args[i]); + } + } + sb.push(")\n"); + } else { + sb.push("\n"); + } + indent(sb, this.indentLevel); + } + + serializeParameter(node: ParameterNode): void { + var sb = this.sb; + var kind = node.parameterKind; + var implicitFieldDeclaration = node.implicitFieldDeclaration; + if (implicitFieldDeclaration) { + this.serializeAccessModifiers(implicitFieldDeclaration); + } + if (kind == ParameterKind.REST) { + sb.push("..."); + } + this.visitIdentifierExpression(node.name); + var type = node.type; + var initializer = node.initializer; + if (type) { + if (kind == ParameterKind.OPTIONAL && !initializer) sb.push("?"); + if (!isTypeOmitted(type)) { + sb.push(": "); + this.visitTypeNode(type); + } + } + if (initializer) { + sb.push(" = "); + this.visitNode(initializer); + } + } + + serializeExternalModifiers(node: DeclarationStatement): void { + var sb = this.sb; + if (node.is(CommonFlags.EXPORT)) { + sb.push("export "); + } else if (node.is(CommonFlags.IMPORT)) { + sb.push("import "); + } else if (node.is(CommonFlags.DECLARE)) { + sb.push("declare "); + } + } + + serializeAccessModifiers(node: DeclarationStatement): void { + var sb = this.sb; + if (node.is(CommonFlags.PUBLIC)) { + sb.push("public "); + } else if (node.is(CommonFlags.PRIVATE)) { + sb.push("private "); + } else if (node.is(CommonFlags.PROTECTED)) { + sb.push("protected "); + } + if (node.is(CommonFlags.STATIC)) { + sb.push("static "); + } else if (node.is(CommonFlags.ABSTRACT)) { + sb.push("abstract "); + } + if (node.is(CommonFlags.READONLY)) { + sb.push("readonly "); + } + } + + finish(): string { + var ret = this.sb.join(""); + this.sb = []; + return ret; + } +} diff --git a/lib/transformer/src/ast.ts b/lib/transformer/src/ast.ts new file mode 100644 index 0000000000..039f5ce2a0 --- /dev/null +++ b/lib/transformer/src/ast.ts @@ -0,0 +1,856 @@ +import { Token } from "./parsing"; + +/** Indicates the kind of a node. */ +export const enum NodeKind { + SOURCE = 0, + NAMEDTYPE = 1, + FUNCTIONTYPE = 2, + TYPENAME = 3, + TYPEPARAMETER = 4, + PARAMETER = 5, + IDENTIFIER = 6, + ASSERTION = 7, + BINARY = 8, + CALL = 9, + CLASS = 10, + COMMA = 11, + ELEMENTACCESS = 12, + FALSE = 13, + FUNCTION = 14, + INSTANCEOF = 15, + LITERAL = 16, + NEW = 17, + NULL = 18, + PARENTHESIZED = 19, + PROPERTYACCESS = 20, + TERNARY = 21, + SUPER = 22, + THIS = 23, + TRUE = 24, + CONSTRUCTOR = 25, + UNARYPOSTFIX = 26, + UNARYPREFIX = 27, + BLOCK = 28, + BREAK = 29, + CONTINUE = 30, + DO = 31, + EMPTY = 32, + EXPORT = 33, + EXPORTDEFAULT = 34, + EXPORTIMPORT = 35, + EXPRESSION = 36, + FOR = 37, + IF = 38, + IMPORT = 39, + RETURN = 40, + SWITCH = 41, + THROW = 42, + TRY = 43, + VARIABLE = 44, + VOID = 45, + WHILE = 46, + CLASSDECLARATION = 47, + ENUMDECLARATION = 48, + ENUMVALUEDECLARATION = 49, + FIELDDECLARATION = 50, + FUNCTIONDECLARATION = 51, + IMPORTDECLARATION = 52, + INDEXSIGNATUREDECLARATION = 53, + INTERFACEDECLARATION = 54, + METHODDECLARATION = 55, + NAMESPACEDECLARATION = 56, + TYPEDECLARATION = 57, + VARIABLEDECLARATION = 58, + DECORATOR = 59, + EXPORTMEMBER = 60, + SWITCHCASE = 61, + COMMENT = 62 +} +/** Checks if a node represents a constant value. */ +declare function nodeIsConstantValue(kind: NodeKind): bool; +/** Checks if a node might be callable. */ +declare function nodeIsCallable(kind: NodeKind): bool; +/** Checks if a node might be callable with generic arguments. */ +declare function nodeIsGenericCallable(kind: NodeKind): bool; +/** Base class of all nodes. */ +export interface Node { + /** Node kind indicator. */ + kind: NodeKind; + /** Source range. */ + range: Range; +} +export interface TypeNode extends Node { + /** Whether nullable or not. */ + isNullable: bool; + /** Tests if this type has a generic component matching one of the given type parameters. */ + hasGenericComponent(typeParameterNodes: TypeParameterNode[]): bool; +} +/** Represents a type name. */ +export interface TypeName extends Node { + /** Identifier of this part. */ + identifier: IdentifierExpression; + /** Next part of the type name or `null` if this is the last part. */ + next: TypeName | null; +} +/** Represents a named type. */ +export interface NamedTypeNode extends TypeNode { + /** Type name. */ + name: TypeName; + /** Type argument references. */ + typeArguments: TypeNode[] | null; +} +/** Represents a function type. */ +export interface FunctionTypeNode extends TypeNode { + /** Accepted parameters. */ + parameters: ParameterNode[]; + /** Return type. */ + returnType: TypeNode; + /** Explicitly provided this type, if any. */ + explicitThisType: NamedTypeNode | null; +} +/** Represents a type parameter. */ +export interface TypeParameterNode extends Node { + /** Identifier reference. */ + name: IdentifierExpression; + /** Extended type reference, if any. */ + extendsType: NamedTypeNode | null; + /** Default type if omitted, if any. */ + defaultType: NamedTypeNode | null; +} +/** Represents the kind of a parameter. */ +export enum ParameterKind { + /** No specific flags. */ + DEFAULT = 0, + /** Is an optional parameter. */ + OPTIONAL = 1, + /** Is a rest parameter. */ + REST = 2 +} +/** Represents a function parameter. */ +export interface ParameterNode extends Node { + /** Parameter kind. */ + parameterKind: ParameterKind; + /** Parameter name. */ + name: IdentifierExpression; + /** Parameter type. */ + type: TypeNode; + /** Initializer expression, if present. */ + initializer: Expression | null; + /** Implicit field declaration, if applicable. */ + implicitFieldDeclaration: FieldDeclaration | null; + /** Common flags indicating specific traits. */ + flags: CommonFlags; + /** Tests if this node has the specified flag or flags. */ + is(flag: CommonFlags): bool; + /** Tests if this node has one of the specified flags. */ + isAny(flag: CommonFlags): bool; + /** Sets a specific flag or flags. */ + set(flag: CommonFlags): void; +} +/** Built-in decorator kinds. */ +export enum DecoratorKind { + CUSTOM = 0, + GLOBAL = 1, + OPERATOR = 2, + OPERATOR_BINARY = 3, + OPERATOR_PREFIX = 4, + OPERATOR_POSTFIX = 5, + UNMANAGED = 6, + SEALED = 7, + INLINE = 8, + EXTERNAL = 9, + BUILTIN = 10, + LAZY = 11, + UNSAFE = 12 +} + +export namespace DecoratorKind { + /** Returns the kind of the specified decorator name node. Defaults to {@link DecoratorKind.CUSTOM}. */ + declare function fromNode(nameNode: Expression): DecoratorKind; +} +/** Represents a decorator. */ +export interface DecoratorNode extends Node { + /** Built-in kind, if applicable. */ + decoratorKind: DecoratorKind; + /** Name expression. */ + name: Expression; + /** Argument expressions. */ + arguments: Array; +} +/** Comment kinds. */ +export const enum CommentKind { + /** Line comment. */ + LINE = 0, + /** Triple-slash comment. */ + TRIPLE = 1, + /** Block comment. */ + BLOCK = 2 +} +/** Represents a comment. */ +export interface CommentNode extends Node { + /** Comment kind. */ + commentKind: CommentKind; + /** Comment text. */ + text: string; +} + +// export type Type = "primitive" | "reference"; + +// export type empty = []; + +// export type Cons = [T, List] + +// export type List = empty | Cons + +// export type IntList = List; + +// export type MakeMaybe = ["Some", T] | ["None"]; + +/** Base class of all expression nodes. */ + +export interface Expression extends Node { + _expressionBrand: void; + // contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution +} + +/** Indicates the kind of a literal. */ +export const enum LiteralKind { + FLOAT = 0, + INTEGER = 1, + STRING = 2, + REGEXP = 3, + ARRAY = 4, + OBJECT = 5 +} + +/** Base class of all literal expressions. */ +export interface LiteralExpression extends Expression { + /** Specific literal kind. */ + literalKind: LiteralKind; +} + +/** Represents an `[]` literal expression. */ +export interface ArrayLiteralExpression extends LiteralExpression { + readonly literalKind: LiteralKind.ARRAY; + /** Nested element expressions. */ + elementExpressions: Array; +} + +/** Represents an integer literal expression. */ +export interface IntegerLiteralExpression extends LiteralExpression { + literalKind: LiteralKind.INTEGER; + /** Integer value. */ + value: I64; +} + +/** Represents an object literal expression. */ +export interface ObjectLiteralExpression extends LiteralExpression { + literalKind: LiteralKind.OBJECT; + + /** Field names. */ + names: Array; + /** Field values. */ + values: Array; +} + +/** Represents a regular expression literal expression. */ +export interface RegexpLiteralExpression extends LiteralExpression { + literalKind: LiteralKind.REGEXP; + + /** Regular expression pattern. */ + pattern: string; + /** Regular expression flags. */ + patternFlags: string; +} + +/** Represents a string literal expression. */ +export interface StringLiteralExpression extends LiteralExpression { + literalKind: LiteralKind.STRING; + /** String value without quotes. */ + value: string; +} + +/** Represents a float literal expression. */ +export interface FloatLiteralExpression extends LiteralExpression { + literalKind: LiteralKind.FLOAT; + + /** Float value. */ + value: f64; +} + +export type Lit = + | IntegerLiteralExpression + | ArrayLiteralExpression + | ObjectLiteralExpression + | RegexpLiteralExpression + | StringLiteralExpression + | FloatLiteralExpression; + +type Symbol = CommonFlags | string; + +// Identifers +/** Represents a `super` expression. */ +export interface SuperExpression extends IdentifierExpression { + text: "super"; +} + +/** Represents a `this` expression. */ +export interface ThisExpression extends IdentifierExpression { + text: "this"; +} + +/** Represents a `true` expression. */ +export interface TrueExpression extends IdentifierExpression { + text: "true"; +} + +/** Represents a `false` expression. */ +export interface FalseExpression extends IdentifierExpression { + text: "false"; +} + +/** Represents a `null` expression. */ +export interface NullExpression extends IdentifierExpression { + text: "null"; +} + +type NULL = IdentifierExpression & { text: "null"; symbol: "null" }; + +/** Represents an identifier expression. */ +export interface IdentifierExpression extends Expression { + /** Textual name. */ + text: string; + /** Symbol. */ + symbol: Symbol; // TODO: symbol + /** Whether quoted or not. */ + isQuoted: bool; +} + +/** Represents a `constructor` expression. */ +export interface ConstructorExpression extends IdentifierExpression { + text: "constructor"; + isQuoted: false; +} + +/** Indicates the kind of an assertion. */ +export enum AssertionKind { + PREFIX, + AS, + NONNULL +} + +/** Represents an assertion expression. */ +export interface AssertionExpression extends Expression { + /** Specific kind of this assertion. */ + assertionKind: AssertionKind; + /** Expression being asserted. */ + expression: Expression; + /** Target type. */ + toType: TypeNode | null; +} + +/** Represents a binary expression. */ +export interface BinaryExpression extends Expression { + /** Operator token. */ + operator: Token; + /** Left-hand side expression */ + left: Expression; + /** Right-hand side expression. */ + right: Expression; +} + +/** Represents a call expression. */ +export interface CallExpression extends Expression { + /** Called expression. Usually an identifier or property access expression. */ + expression: Expression; + /** Provided type arguments. */ + typeArguments: TypeNode[] | null; + /** Provided arguments. */ + arguments: Expression[]; + + /** Gets the type arguments range for reporting. */ + typeArgumentsRange(): Range; + + /** Gets the arguments range for reporting. */ + argumentsRange(): Range; +} + +/** Represents a class expression using the 'class' keyword. */ +export interface ClassExpression extends Expression { + /** Inline class declaration. */ + declaration: ClassDeclaration; +} + +/** Represents a comma expression composed of multiple expressions. */ +export interface CommaExpression extends Expression { + /** Sequential expressions. */ + expressions: Expression[]; +} + +/** Represents an element access expression, e.g., array access. */ +export interface ElementAccessExpression extends Expression { + /** Expression being accessed. */ + expression: Expression; + /** Element of the expression being accessed. */ + elementExpression: Expression; +} + +/** Represents a function expression using the 'function' keyword. */ +export interface FunctionExpression extends Expression { + /** Inline function declaration. */ + declaration: FunctionDeclaration; +} + +/** Represents an `instanceof` expression. */ +export interface InstanceOfExpression extends Expression { + /** Expression being asserted. */ + expression: Expression; + /** Type to test for. */ + isType: TypeNode; +} + +/** Represents a `new` expression. Like a call but with its own kind. */ +export interface NewExpression extends CallExpression {} + +/** Represents a parenthesized expression. */ +export interface ParenthesizedExpression extends Expression { + /** Expression in parenthesis. */ + expression: Expression; +} + +/** Represents a property access expression. */ +export interface PropertyAccessExpression extends Expression { + /** Expression being accessed. */ + expression: Expression; + /** Property of the expression being accessed. */ + property: IdentifierExpression; +} + +/** Represents a ternary expression, i.e., short if notation. */ +export interface TernaryExpression extends Expression { + /** Condition expression. */ + condition: Expression; + /** Expression executed when condition is `true`. */ + ifThen: Expression; + /** Expression executed when condition is `false`. */ + ifElse: Expression; +} + +/** Base class of all unary expressions. */ +export interface UnaryExpression extends Expression { + /** Operator token. */ + operator: Token; + /** Operand expression. */ + operand: Expression; +} + +/** Represents a unary postfix expression, e.g. a postfix increment. */ +export interface UnaryPostfixExpression extends UnaryExpression {} + +/** Represents a unary prefix expression, e.g. a negation. */ +export interface UnaryPrefixExpression extends UnaryExpression {} + +/** Base cAlass of all statement nodes. */ +export interface Statement extends Node {} + +/** Indicates the specific kind of a source. */ +export enum SourceKind { + /** User-provided file. */ + USER = 0, + /** User-provided entry file. */ + USER_ENTRY = 1, + /** Library-provided file. */ + LIBRARY = 2, + /** Library-provided entry file. */ + LIBRARY_ENTRY = 3 +} +/** A top-level source node. */ +export interface Source extends Node { + parent: null; + /** Source kind. */ + sourceKind: SourceKind; + /** Normalized path with file extension. */ + normalizedPath: string; + /** Path used internally. */ + internalPath: string; + /** Simple path (last part without extension). */ + simplePath: string; + /** Contained statements. */ + statements: Statement[]; + /** Full source text. */ + text: string; + /** Source map index. */ + debugInfoIndex: i32; + /** Re-exported sources. */ + exportPaths: Set | null; + /** Constructs a new source node. */ + new (normalizedPath: string, text: string, kind: SourceKind): Source; + readonly isLibrary: bool; +} + +export interface Range { + source: Source; + start: i32; + end: i32; + readonly atStart: Range; + readonly atEnd: Range; + readonly line: i32; + readonly column: i32; + toString(): string; + debugInfoRef: usize; +} + +/** Base class of all declaration statements. */ +export interface DeclarationStatement extends Statement { + /** Simple name being declared. */ + name: IdentifierExpression; + /** Array of decorators. */ + decorators: DecoratorNode[] | null; + /** Common flags indicating specific traits. */ + flags: CommonFlags; + /** Tests if this node has the specified flag or flags. */ + is(flag: CommonFlags): bool; + /** Tests if this node has one of the specified flags. */ + isAny(flag: CommonFlags): bool; + /** Sets a specific flag or flags. */ + set(flag: CommonFlags): void; +} +/** Represents an index signature declaration. */ +export interface IndexSignatureDeclaration extends DeclarationStatement { + /** Key type. */ + keyType: NamedTypeNode; + /** Value type. */ + valueType: TypeNode; +} +/** Base class of all variable-like declaration statements. */ +export interface VariableLikeDeclarationStatement extends DeclarationStatement { + /** Variable type. */ + type: TypeNode | null; + /** Variable initializer. */ + initializer: Expression | null; +} +/** Represents a block statement. */ +export interface BlockStatement extends Statement { + /** Contained statements. */ + statements: Statement[]; +} +/** Represents a `break` statement. */ +export interface BreakStatement extends Statement { + /** Target label, if applicable. */ + label: IdentifierExpression | null; +} +/** Represents a `class` declaration. */ +export interface ClassDeclaration extends DeclarationStatement { + /** Accepted type parameters. */ + typeParameters: TypeParameterNode[] | null; + /** Base class type being extended, if any. */ + extendsType: NamedTypeNode | null; + /** Interface types being implemented, if any. */ + implementsTypes: NamedTypeNode[] | null; + /** Class member declarations. */ + members: DeclarationStatement[]; + readonly isGeneric: bool; +} +/** Represents a `continue` statement. */ +export interface ContinueStatement extends Statement { + /** Target label, if applicable. */ + label: IdentifierExpression | null; +} +/** Represents a `do` statement. */ +export interface DoStatement extends Statement { + /** Statement being looped over. */ + statement: Statement; + /** Condition when to repeat. */ + condition: Expression; +} +/** Represents an empty statement, i.e., a semicolon terminating nothing. */ +export interface EmptyStatement extends Statement {} +/** Represents an `enum` declaration. */ +export interface EnumDeclaration extends DeclarationStatement { + /** Enum value declarations. */ + values: EnumValueDeclaration[]; +} +/** Represents a value of an `enum` declaration. */ +export interface EnumValueDeclaration extends VariableLikeDeclarationStatement { + /** Value expression. */ + value: Expression | null; +} +/** Represents an `export import` statement of an interface. */ +export interface ExportImportStatement extends Node { + /** Identifier being imported. */ + name: IdentifierExpression; + /** Identifier being exported. */ + externalName: IdentifierExpression; +} +/** Represents a member of an `export` statement. */ +export interface ExportMember extends Node { + /** Local identifier. */ + localName: IdentifierExpression; + /** Exported identifier. */ + exportedName: IdentifierExpression; +} +/** Represents an `export` statement. */ +export interface ExportStatement extends Statement { + /** Array of members if a set of named exports, or `null` if a file export. */ + members: ExportMember[] | null; + /** Path being exported from, if applicable. */ + path: StringLiteralExpression | null; + /** Internal path being referenced, if `path` is set. */ + internalPath: string | null; + /** Whether this is a declared export. */ + isDeclare: bool; +} +/** Represents an `export default` statement. */ +export interface ExportDefaultStatement extends Statement { + /** Declaration being exported as default. */ + declaration: DeclarationStatement; +} +/** Represents an expression that is used as a statement. */ +export interface ExpressionStatement extends Statement { + /** Expression being used as a statement.*/ + expression: Expression; +} +/** Represents a field declaration within a `class`. */ +export interface FieldDeclaration extends VariableLikeDeclarationStatement { + /** Parameter index if declared as a constructor parameter, otherwise `-1`. */ + parameterIndex: i32; +} +/** Represents a `for` statement. */ +export interface ForStatement extends Statement { + /** + * Initializer statement, if present. + * Either a {@link VariableStatement} or {@link ExpressionStatement}. + */ + initializer: Statement | null; + /** Condition expression, if present. */ + condition: Expression | null; + /** Incrementor expression, if present. */ + incrementor: Expression | null; + /** Statement being looped over. */ + statement: Statement; +} +/** Indicates the kind of an array function. */ +export const enum ArrowKind { + /** Not an arrow function. */ + NONE = 0, + /** Parenthesized parameter list. */ + ARROW_PARENTHESIZED = 1, + /** Single parameter without parenthesis. */ + ARROW_SINGLE = 2 +} +/** Represents a `function` declaration. */ +export interface FunctionDeclaration extends DeclarationStatement { + /** Type parameters, if any. */ + typeParameters: TypeParameterNode[] | null; + /** Function signature. */ + signature: FunctionTypeNode; + /** Body statement. Usually a block. */ + body: Statement | null; + /** Arrow function kind, if applicable. */ + arrowKind: ArrowKind; + readonly isGeneric: bool; + /** Clones this function declaration. */ + clone(): FunctionDeclaration; +} +/** Represents an `if` statement. */ +export interface IfStatement extends Statement { + /** Condition. */ + condition: Expression; + /** Statement executed when condition is `true`. */ + ifTrue: Statement; + /** Statement executed when condition is `false`. */ + ifFalse: Statement | null; +} +/** Represents an `import` declaration part of an {@link ImportStatement}. */ +export interface ImportDeclaration extends DeclarationStatement { + /** Identifier being imported. */ + foreignName: IdentifierExpression; +} +/** Represents an `import` statement. */ +export interface ImportStatement extends Statement { + /** Array of member declarations or `null` if an asterisk import. */ + declarations: ImportDeclaration[] | null; + /** Name of the local namespace, if an asterisk import. */ + namespaceName: IdentifierExpression | null; + /** Path being imported from. */ + path: StringLiteralExpression; + /** Internal path being referenced. */ + internalPath: string; +} +/** Represents an `interfarce` declaration. */ +export interface InterfaceDeclaration extends ClassDeclaration {} +/** Represents a method declaration within a `class`. */ +export interface MethodDeclaration extends FunctionDeclaration {} +/** Represents a `namespace` declaration. */ +export interface NamespaceDeclaration extends DeclarationStatement { + /** Array of namespace members. */ + members: Statement[]; +} +/** Represents a `return` statement. */ +export interface ReturnStatement extends Statement { + /** Value expression being returned, if present. */ + value: Expression | null; +} +/** Represents a single `case` within a `switch` statement. */ +export interface SwitchCase extends Node { + /** Label expression. `null` indicates the default case. */ + label: Expression | null; + /** Contained statements. */ + statements: Statement[]; +} +/** Represents a `switch` statement. */ +export interface SwitchStatement extends Statement { + /** Condition expression. */ + condition: Expression; + /** Contained cases. */ + cases: SwitchCase[]; +} +/** Represents a `throw` statement. */ +export interface ThrowStatement extends Statement { + /** Value expression being thrown. */ + value: Expression; +} +/** Represents a `try` statement. */ +export interface TryStatement extends Statement { + /** Contained statements. */ + statements: Statement[]; + /** Exception variable name, if a `catch` clause is present. */ + catchVariable: IdentifierExpression | null; + /** Statements being executed on catch, if a `catch` clause is present. */ + catchStatements: Statement[] | null; + /** Statements being executed afterwards, if a `finally` clause is present. */ + finallyStatements: Statement[] | null; +} +/** Represents a `type` declaration. */ +export interface TypeDeclaration extends DeclarationStatement { + /** Type parameters, if any. */ + typeParameters: TypeParameterNode[] | null; + /** Type being aliased. */ + type: TypeNode; +} +/** Represents a variable declaration part of a {@link VariableStatement}. */ +export interface VariableDeclaration extends VariableLikeDeclarationStatement {} +/** Represents a variable statement wrapping {@link VariableDeclaration}s. */ +export interface VariableStatement extends Statement { + /** Array of decorators. */ + decorators: DecoratorNode[] | null; + /** Array of member declarations. */ + declarations: VariableDeclaration[]; +} +/** Represents a void statement dropping an expression's value. */ +export interface VoidStatement extends Statement { + /** Expression being dropped. */ + expression: Expression; +} +/** Represents a `while` statement. */ +export interface WhileStatement extends Statement { + /** Condition expression. */ + condition: Expression; + /** Statement being looped over. */ + statement: Statement; +} + +/** Finds the first decorator matching the specified kind. */ +export function findDecorator( + kind: DecoratorKind, + decorators: DecoratorNode[] | null +): DecoratorNode | null { + if (decorators) { + for (let i = 0, k = decorators.length; i < k; ++i) { + let decorator = decorators[i]; + if (decorator.decoratorKind == kind) return decorator; + } + } + return null; +} + +/** Mangles an external to an internal path. */ +export function mangleInternalPath(path: string): string { + if (path.endsWith(".ts")) path = path.substring(0, path.length - 3); + return path; +} + +/** Tests if the specified type node represents an omitted type. */ +export function isTypeOmitted(type: TypeNode): bool { + if (type.kind == NodeKind.NAMEDTYPE) { + let name = (type).name; + return !(name.next || name.identifier.text.length); + } + return false; +} + +/** Indicates traits of a {@link Node} or {@link Element}. */ +export const enum CommonFlags { + /** No flags set. */ + NONE = 0, + + // Basic modifiers + + /** Has an `import` modifier. */ + IMPORT = 1 << 0, + /** Has an `export` modifier. */ + EXPORT = 1 << 1, + /** Has a `declare` modifier. */ + DECLARE = 1 << 2, + /** Has a `const` modifier. */ + CONST = 1 << 3, + /** Has a `let` modifier. */ + LET = 1 << 4, + /** Has a `static` modifier. */ + STATIC = 1 << 5, + /** Has a `readonly` modifier. */ + READONLY = 1 << 6, + /** Has an `abstract` modifier. */ + ABSTRACT = 1 << 7, + /** Has a `public` modifier. */ + PUBLIC = 1 << 8, + /** Has a `private` modifier. */ + PRIVATE = 1 << 9, + /** Has a `protected` modifier. */ + PROTECTED = 1 << 10, + /** Has a `get` modifier. */ + GET = 1 << 11, + /** Has a `set` modifier. */ + SET = 1 << 12, + /** Has a definite assignment assertion `!` as in `x!: i32;`. */ + DEFINITE_ASSIGNMENT = 1 << 13, + + // Extended modifiers usually derived from basic modifiers + + /** Is ambient, that is either declared or nested in a declared element. */ + AMBIENT = 1 << 14, + /** Is generic. */ + GENERIC = 1 << 15, + /** Is part of a generic context. */ + GENERIC_CONTEXT = 1 << 16, + /** Is an instance member. */ + INSTANCE = 1 << 17, + /** Is a constructor. */ + CONSTRUCTOR = 1 << 18, + /** Is a module export. */ + MODULE_EXPORT = 1 << 19, + /** Is a module import. */ + MODULE_IMPORT = 1 << 20, + + // Compilation states + + /** Is resolved. */ + RESOLVED = 1 << 21, + /** Is compiled. */ + COMPILED = 1 << 22, + /** Has a constant value and is therefore inlined. */ + INLINED = 1 << 23, + /** Is scoped. */ + SCOPED = 1 << 24, + /** Is a trampoline. */ + TRAMPOLINE = 1 << 25, + /** Is a virtual method. */ + VIRTUAL = 1 << 26, + /** Is the main function. */ + MAIN = 1 << 27, + + // Other + + /** Is quoted. */ + QUOTED = 1 << 28 +} diff --git a/lib/transformer/src/base.ts b/lib/transformer/src/base.ts new file mode 100644 index 0000000000..87efc7e027 --- /dev/null +++ b/lib/transformer/src/base.ts @@ -0,0 +1,652 @@ +import { NodeKind, Node, Source, NamedTypeNode, FunctionTypeNode, TypeName, TypeParameterNode, IdentifierExpression, AssertionExpression, BinaryExpression, CallExpression, ClassExpression, ElementAccessExpression, FunctionExpression, InstanceOfExpression, LiteralExpression, NewExpression, ParenthesizedExpression, PropertyAccessExpression, TernaryExpression, UnaryPostfixExpression, UnaryPrefixExpression, BlockStatement, BreakStatement, ContinueStatement, DoStatement, EmptyStatement, ExportStatement, ExportDefaultStatement, ExportImportStatement, ExpressionStatement, ForStatement, IfStatement, ImportStatement, ReturnStatement, SwitchStatement, ThrowStatement, TryStatement, VariableStatement, WhileStatement, ClassDeclaration, EnumDeclaration, EnumValueDeclaration, FieldDeclaration, FunctionDeclaration, ImportDeclaration, IndexSignatureDeclaration, InterfaceDeclaration, MethodDeclaration, NamespaceDeclaration, TypeDeclaration, VariableDeclaration, DecoratorNode, ExportMember, ParameterNode, SwitchCase, TypeNode, ArrayLiteralExpression, Expression, ObjectLiteralExpression, FloatLiteralExpression, StringLiteralExpression, RegexpLiteralExpression, UnaryExpression, SuperExpression, FalseExpression, TrueExpression, ThisExpression, NullExpression, ConstructorExpression, Statement, VoidStatement, CommentNode, CommaExpression, IntegerLiteralExpression } from "./ast"; +import { AbstractVisitor } from "./visitor"; + +export class BaseVisitor extends AbstractVisitor { + depth: number = 0; + + _visit(node: Node): void { + switch (node.kind) { + case NodeKind.SOURCE: { + this.visitSource(node); + break; + } + + // types + + case NodeKind.NAMEDTYPE: { + this.visitNamedTypeNode(node); + break; + } + case NodeKind.FUNCTIONTYPE: { + this.visitFunctionTypeNode(node); + break; + } + case NodeKind.TYPENAME: { + this.visitTypeName(node); + } + case NodeKind.TYPEPARAMETER: { + this.visitTypeParameter(node); + break; + } + + // expressions + + case NodeKind.FALSE: + case NodeKind.NULL: + case NodeKind.SUPER: + case NodeKind.THIS: + case NodeKind.TRUE: + case NodeKind.CONSTRUCTOR: + case NodeKind.IDENTIFIER: { + this.visitIdentifierExpression(node); + break; + } + case NodeKind.ASSERTION: { + this.visitAssertionExpression(node); + break; + } + case NodeKind.BINARY: { + this.visitBinaryExpression(node); + break; + } + case NodeKind.CALL: { + this.visitCallExpression(node); + break; + } + case NodeKind.CLASS: { + this.visitClassExpression(node); + break; + } + case NodeKind.COMMA: { + this.visitCommaExpression(node); + break; + } + case NodeKind.ELEMENTACCESS: { + this.visitElementAccessExpression(node); + break; + } + case NodeKind.FUNCTION: { + this.visitFunctionExpression(node); + break; + } + case NodeKind.INSTANCEOF: { + this.visitInstanceOfExpression(node); + break; + } + case NodeKind.LITERAL: { + this.visitLiteralExpression(node); + break; + } + case NodeKind.NEW: { + this.visitNewExpression(node); + break; + } + case NodeKind.PARENTHESIZED: { + this.visitParenthesizedExpression(node); + break; + } + case NodeKind.PROPERTYACCESS: { + this.visitPropertyAccessExpression(node); + break; + } + case NodeKind.TERNARY: { + this.visitTernaryExpression(node); + break; + } + case NodeKind.UNARYPOSTFIX: { + this.visitUnaryPostfixExpression(node); + break; + } + case NodeKind.UNARYPREFIX: { + this.visitUnaryPrefixExpression(node); + break; + } + + // statements + + case NodeKind.BLOCK: { + this.visitBlockStatement(node); + break; + } + case NodeKind.BREAK: { + this.visitBreakStatement(node); + break; + } + case NodeKind.CONTINUE: { + this.visitContinueStatement(node); + break; + } + case NodeKind.DO: { + this.visitDoStatement(node); + break; + } + case NodeKind.EMPTY: { + this.visitEmptyStatement(node); + break; + } + case NodeKind.EXPORT: { + this.visitExportStatement(node); + break; + } + case NodeKind.EXPORTDEFAULT: { + this.visitExportDefaultStatement(node); + break; + } + case NodeKind.EXPORTIMPORT: { + this.visitExportImportStatement(node); + break; + } + case NodeKind.EXPRESSION: { + this.visitExpressionStatement(node); + break; + } + case NodeKind.FOR: { + this.visitForStatement(node); + break; + } + case NodeKind.IF: { + this.visitIfStatement(node); + break; + } + case NodeKind.IMPORT: { + this.visitImportStatement(node); + break; + } + case NodeKind.RETURN: { + this.visitReturnStatement(node); + break; + } + case NodeKind.SWITCH: { + this.visitSwitchStatement(node); + break; + } + case NodeKind.THROW: { + this.visitThrowStatement(node); + break; + } + case NodeKind.TRY: { + this.visitTryStatement(node); + break; + } + case NodeKind.VARIABLE: { + this.visitVariableStatement(node); + break; + } + case NodeKind.WHILE: { + this.visitWhileStatement(node); + break; + } + + // declaration statements + + case NodeKind.CLASSDECLARATION: { + this.visitClassDeclaration(node); + break; + } + case NodeKind.ENUMDECLARATION: { + this.visitEnumDeclaration(node); + break; + } + case NodeKind.ENUMVALUEDECLARATION: { + this.visitEnumValueDeclaration(node); + break; + } + case NodeKind.FIELDDECLARATION: { + this.visitFieldDeclaration(node); + break; + } + case NodeKind.FUNCTIONDECLARATION: { + this.visitFunctionDeclaration(node); + break; + } + case NodeKind.IMPORTDECLARATION: { + this.visitImportDeclaration(node); + break; + } + case NodeKind.INDEXSIGNATUREDECLARATION: { + this.visitIndexSignatureDeclaration(node); + break; + } + case NodeKind.INTERFACEDECLARATION: { + this.visitInterfaceDeclaration(node); + break; + } + case NodeKind.METHODDECLARATION: { + this.visitMethodDeclaration(node); + break; + } + case NodeKind.NAMESPACEDECLARATION: { + this.visitNamespaceDeclaration(node); + break; + } + case NodeKind.TYPEDECLARATION: { + this.visitTypeDeclaration(node); + break; + } + case NodeKind.VARIABLEDECLARATION: { + this.visitVariableDeclaration(node); + break; + } + + // other + + case NodeKind.DECORATOR: { + this.visitDecoratorNode(node); + break; + } + case NodeKind.EXPORTMEMBER: { + this.visitExportMember(node); + break; + } + case NodeKind.PARAMETER: { + this.visitParameter(node); + break; + } + case NodeKind.SWITCHCASE: { + this.visitSwitchCase(node); + break; + } + default: assert(false); + } + } + + visitSource(node: Source): void { + for (const stmt of node.statements) { + this.depth++; + this.visit(stmt); + this.depth--; + } + } + + visitTypeNode(node: TypeNode): void {} + + visitTypeName(node: TypeName): void { + this.visit(node.identifier); + if (node.next) { + this.visit(node.next); + } + } + + visitNamedTypeNode(node: NamedTypeNode): void { + this.visit(node.name); + this.visit(node.typeArguments); + } + + visitFunctionTypeNode(node: FunctionTypeNode): void { + for (let param of node.parameters) { + this.visit(param); + } + this.visit(node.returnType); + } + + visitTypeParameter(node: TypeParameterNode): void { + this.visit(node.name); + if (node.extendsType) this.visit(node.extendsType); + if (node.defaultType) this.visit(node.defaultType); + } + + visitIdentifierExpression(node: IdentifierExpression): void {} + + visitArrayLiteralExpression(node: ArrayLiteralExpression): void { + node.elementExpressions.map((e: Expression) => { + if (e) this.visit(e); + }); + } + + visitObjectLiteralExpression(node: ObjectLiteralExpression): void { + if (node.values && node.names) { + assert(node.values.length == node.names.length); + for (let i = 0; i < node.values.length; i++) { + this.visit(node.names[i]); + this.visit(node.values[i]); + } + } + } + + visitAssertionExpression(node: AssertionExpression): void { + if (node.toType) this.visit(node.toType); + this.visit(node.expression); + } + + visitBinaryExpression(node: BinaryExpression): void { + this.visit(node.left); + this.visit(node.right); + } + + visitCallExpression(node: CallExpression): void { + this.visit(node.expression); + this.visit(node.typeArguments); + this.visit(node.arguments); + } + + visitClassExpression(node: ClassExpression): void { + this.visit(node.declaration); + } + + visitCommaExpression(node: CommaExpression): void { + this.visit(node.expressions); + } + + visitElementAccessExpression(node: ElementAccessExpression): void { + this.visit(node.elementExpression); + this.visit(node.expression); + } + + visitFunctionExpression(node: FunctionExpression): void { + this.visit(node.declaration); + } + + visitLiteralExpression(node: LiteralExpression): void { + // node. + } + + visitFloatLiteralExpression(node: FloatLiteralExpression): void {} + + visitInstanceOfExpression(node: InstanceOfExpression): void { + this.visit(node.expression); + this.visit(node.isType); + } + + visitIntegerLiteralExpression(node: IntegerLiteralExpression): void {} + + visitStringLiteral(str: string, singleQuoted?: boolean): void {} + + visitStringLiteralExpression(node: StringLiteralExpression): void {} + + visitRegexpLiteralExpression(node: RegexpLiteralExpression): void {} + + visitNewExpression(node: NewExpression): void { + this.visit(node.expression); + this.visit(node.typeArguments); + this.visit(node.arguments); + } + + visitParenthesizedExpression(node: ParenthesizedExpression): void { + this.visit(node.expression); + } + + visitPropertyAccessExpression(node: PropertyAccessExpression): void { + this.visit(node.property); + this.visit(node.expression); + } + + visitTernaryExpression(node: TernaryExpression): void { + this.visit(node.condition); + this.visit(node.ifThen); + this.visit(node.ifElse); + } + + visitUnaryExpression(node: UnaryExpression): void { + this.visit(node.operand); + } + + visitUnaryPostfixExpression(node: UnaryPostfixExpression): void { + this.visit(node.operand); + } + + visitUnaryPrefixExpression(node: UnaryPrefixExpression): void { + this.visit(node.operand); + } + + visitSuperExpression(node: SuperExpression): void {} + + visitFalseExpression(node: FalseExpression): void {} + + visitTrueExpression(node: TrueExpression): void {} + + visitThisExpression(node: ThisExpression): void {} + + visitNullExperssion(node: NullExpression): void {} + + visitConstructorExpression(node: ConstructorExpression): void {} + + visitNodeAndTerminate(statement: Statement): void {} + + visitBlockStatement(node: BlockStatement): void { + this.depth++; + this.visit(node.statements); + this.depth--; + } + + visitBreakStatement(node: BreakStatement): void { + if (node.label) { + this.visit(node.label); + } + } + + visitContinueStatement(node: ContinueStatement): void { + if (node.label) { + this.visit(node.label); + } + } + + visitClassDeclaration(node: ClassDeclaration, isDefault?: boolean): void { + this.visit(node.name); + this.depth++; + this.visit(node.decorators); + assert( + node.isGeneric ? node.typeParameters != null : node.typeParameters == null + ); + if (node.isGeneric) { + this.visit(node.typeParameters); + } + if (node.extendsType) { + this.visit(node.extendsType); + } + this.visit(node.implementsTypes); + this.visit(node.members); + this.depth--; + } + + visitDoStatement(node: DoStatement): void { + this.visit(node.condition); + this.visit(node.statement); + } + + visitEmptyStatement(node: EmptyStatement): void {} + + visitEnumDeclaration(node: EnumDeclaration, isDefault?: boolean): void { + this.visit(node.name); + this.visit(node.decorators); + this.visit(node.values); + } + + visitEnumValueDeclaration(node: EnumValueDeclaration): void { + this.visit(node.name); + if (node.initializer) { + this.visit(node.initializer); + } + } + + visitExportImportStatement(node: ExportImportStatement): void { + this.visit(node.name); + this.visit(node.externalName); + } + + visitExportMember(node: ExportMember): void { + this.visit(node.localName); + this.visit(node.exportedName); + } + + visitExportStatement(node: ExportStatement): void { + if (node.path) { + this.visit(node.path); + } + this.visit(node.members); + } + + visitExportDefaultStatement(node: ExportDefaultStatement): void { + this.visit(node.declaration); + } + + visitExpressionStatement(node: ExpressionStatement): void { + this.visit(node.expression); + } + + visitFieldDeclaration(node: FieldDeclaration): void { + this.visit(node.name); + if (node.type) { + this.visit(node.type); + } + if (node.initializer) { + this.visit(node.initializer); + } + this.visit(node.decorators); + } + + visitForStatement(node: ForStatement): void { + if (node.initializer) this.visit(node.initializer); + if (node.condition) this.visit(node.condition); + if (node.incrementor) this.visit(node.incrementor); + this.visit(node.statement); + } + + visitFunctionDeclaration( + node: FunctionDeclaration, + isDefault?: boolean + ): void { + this.visit(node.name); + this.visit(node.decorators); + if (node.isGeneric) { + this.visit(node.typeParameters); + } + this.visit(node.signature); + this.depth++; + if (node.body) this.visit(node.body); + this.depth--; + } + + visitFunctionCommon(node: FunctionDeclaration): void { + // this.visit(node.name) + } + + visitIfStatement(node: IfStatement): void { + this.visit(node.condition); + this.visit(node.ifTrue); + if (node.ifFalse) this.visit(node.ifFalse); + } + + visitImportDeclaration(node: ImportDeclaration): void { + this.visit(node.foreignName); + this.visit(node.name); + this.visit(node.decorators); + } + + visitImportStatement(node: ImportStatement): void { + if (node.namespaceName) this.visit(node.namespaceName); + this.visit(node.declarations); + } + + visitIndexSignatureDeclaration(node: IndexSignatureDeclaration): void { + // this.visit(node.name); + // this.visit(node.keyType); + // this.visit(node.valueType); + } + + visitInterfaceDeclaration( + node: InterfaceDeclaration, + isDefault?: boolean + ): void { + this.visit(node.name); + if (node.isGeneric) { + this.visit(node.typeParameters); + } + this.visit(node.implementsTypes); + if (node.extendsType) this.visit(node.extendsType); + this.depth++; + this.visit(node.members); + this.depth--; + } + + visitMethodDeclaration(node: MethodDeclaration): void { + this.visit(node.name); + if (node.isGeneric) { + this.visit(node.typeParameters); + } + this.visit(node.signature); + this.visit(node.decorators); + this.depth++; + if (node.body) this.visit(node.body); + this.depth--; + } + + visitNamespaceDeclaration( + node: NamespaceDeclaration, + isDefault?: boolean + ): void { + this.visit(node.name); + this.visit(node.decorators); + this.visit(node.members); + } + + visitReturnStatement(node: ReturnStatement): void { + if (node.value) this.visit(node.value); + } + + visitSwitchCase(node: SwitchCase): void { + if (node.label) this.visit(node.label); + this.visit(node.statements); + } + + visitSwitchStatement(node: SwitchStatement): void { + this.visit(node.condition); + this.depth++; + this.visit(node.cases); + this.depth--; + } + + visitThrowStatement(node: ThrowStatement): void { + this.visit(node.value); + } + + visitTryStatement(node: TryStatement): void { + this.visit(node.statements); + if (node.catchVariable) this.visit(node.catchVariable); + this.visit(node.catchStatements); + this.visit(node.finallyStatements); + } + + visitTypeDeclaration(node: TypeDeclaration): void { + this.visit(node.name); + this.visit(node.decorators); + this.visit(node.type); + this.visit(node.typeParameters); + } + + visitVariableDeclaration(node: VariableDeclaration): void { + this.visit(node.name); + if (node.type) this.visit(node.type); + if (node.initializer) this.visit(node.initializer); + } + + visitVariableStatement(node: VariableStatement): void { + this.visit(node.decorators); + this.visit(node.declarations); + } + + visitWhileStatement(node: WhileStatement): void { + this.visit(node.condition); + this.depth++; + this.visit(node.statement); + this.depth--; + } + + visitVoidStatement(node: VoidStatement): void {} + + visitComment(node: CommentNode): void {} + + visitDecoratorNode(node: DecoratorNode): void { + this.visit(node.name); + this.visit(node.arguments); + } + + visitParameter(node: ParameterNode): void { + this.visit(node.name); + if (node.implicitFieldDeclaration) { + this.visit(node.implicitFieldDeclaration); + } + if (node.initializer) this.visit(node.initializer); + this.visit(node.type); + } + +} diff --git a/lib/transformer/src/compiler.ts b/lib/transformer/src/compiler.ts new file mode 100644 index 0000000000..a440852377 --- /dev/null +++ b/lib/transformer/src/compiler.ts @@ -0,0 +1,12 @@ +import { Source } from "./ast"; + +export interface Program { + sources: Source[]; +} + +export interface Parser { + donelog: Set; + seenlog: Set; + program: Program; + parseFile(sourceText: string, path: string, isEntry: boolean): void; +} \ No newline at end of file diff --git a/lib/transformer/src/examples/functions.ts b/lib/transformer/src/examples/functions.ts new file mode 100644 index 0000000000..db394b4a4a --- /dev/null +++ b/lib/transformer/src/examples/functions.ts @@ -0,0 +1,45 @@ +import { SourceKind, FunctionDeclaration, Node, MethodDeclaration, ClassDeclaration } from '../ast'; +import { Transformer } from "../transformer"; +import { BaseVisitor } from "../base"; + +/** + * Example of using a transformer to print the AST for each entry file + */ +export default class FunctionLister extends Transformer { + afterParse(): void { + const files = this.parser.program.sources.filter( + _source => _source.sourceKind == SourceKind.USER_ENTRY + ); + files.forEach(source => { + // Create a string of source rebuilt from the AST node `source` + this.stdout(FunctionVisitor.visit(source) + "\n"); + }); + } +} + +class FunctionVisitor extends BaseVisitor { + funcsFound: string[] = []; + currentClass: string; + + visitClassDeclaration(node: ClassDeclaration): void { + // Remember current class + this.currentClass = node.name.text; + // Important to pass call parent visitor if you want to visit child nodes + super.visitClassDeclaration(node); + // Note can't call `super.visit(node) because it will call this function again. + } + + visitFunctionDeclaration(node: FunctionDeclaration): void { + this.funcsFound.push("Function: " + node.name.text); + } + + visitMethodDeclaration(node: MethodDeclaration): void { + this.funcsFound.push("Method: " + this.currentClass + "." + node.name.text); + } + + static visit(node: Node): string { + const visitor = new FunctionVisitor(); + visitor.visit(node); + return visitor.funcsFound.join("\n"); + } +} diff --git a/lib/transformer/src/examples/index.ts b/lib/transformer/src/examples/index.ts new file mode 100644 index 0000000000..8774d07509 --- /dev/null +++ b/lib/transformer/src/examples/index.ts @@ -0,0 +1,2 @@ +export * from "./functions"; +export * from "./printer"; diff --git a/lib/transformer/src/examples/printer.ts b/lib/transformer/src/examples/printer.ts new file mode 100644 index 0000000000..54126a6302 --- /dev/null +++ b/lib/transformer/src/examples/printer.ts @@ -0,0 +1,19 @@ +import { ASTBuilder } from "../ASTBuilder"; +import { SourceKind } from "../ast"; +import { Transformer } from "../transformer"; + +/** + * Example of using a transformer to print the AST for each entry file + */ +export default class Printer extends Transformer { + afterParse(): void { + const files = this.parser.program.sources.filter( + _source => _source.sourceKind == SourceKind.USER_ENTRY + ); + files.forEach(source => { + // Create a string of source rebuilt from the AST node `source` + const sourceText: string = ASTBuilder.build(source); + this.stdout(sourceText); + }); + } +} diff --git a/lib/transformer/src/index.ts b/lib/transformer/src/index.ts new file mode 100644 index 0000000000..22e9d14b6d --- /dev/null +++ b/lib/transformer/src/index.ts @@ -0,0 +1,7 @@ +export * from "./compiler"; +export * from "./transformer"; +export * from "./ASTBuilder"; +export * from "./visitor"; +export * from "./base"; +export * from "./examples"; +export * from "./ast"; diff --git a/lib/transformer/src/parsing.ts b/lib/transformer/src/parsing.ts new file mode 100644 index 0000000000..7d7aba7241 --- /dev/null +++ b/lib/transformer/src/parsing.ts @@ -0,0 +1,194 @@ + +/** Named token types. */ +export const enum Token { + + // keywords + // discarded: ANY, BOOLEAN, NEVER, NUMBER, STRING, SYMBOL, UNDEFINED, LESSTHAN_SLASH + + ABSTRACT, + AS, + ASYNC, + AWAIT, // ES2017 + BREAK, // ES2017 + CASE, // ES2017 + CATCH, // ES2017 + CLASS, // ES2017 + CONST, // ES2017 + CONTINUE, // ES2017 + CONSTRUCTOR, + DEBUGGER, // ES2017 + DECLARE, + DEFAULT, // ES2017 + DELETE, // ES2017 + DO, // ES2017 + ELSE, // ES2017 + ENUM, // ES2017 future + EXPORT, // ES2017 + EXTENDS, // ES2017 + FALSE, // ES + FINALLY, // ES2017 + FOR, // ES2017 + FROM, // AS possible identifier + FUNCTION, // ES2017 + GET, + IF, // ES2017 + IMPLEMENTS, // ES2017 non-lexical + IMPORT, // ES2017 + IN, // ES2017 + INSTANCEOF, // ES2017 + INTERFACE, // ES2017 non-lexical + IS, + KEYOF, + LET, // ES2017 non-lexical + MODULE, // AS possible identifier + NAMESPACE, // AS possible identifier + NEW, // ES2017 + NULL, // ES + OF, + PACKAGE, // ES2017 non-lexical + PRIVATE, // ES2017 non-lexical + PROTECTED, // ES2017 non-lexical + PUBLIC, // ES2017 non-lexical + READONLY, + RETURN, // ES2017 + SET, + STATIC, // ES2017 non-lexical + SUPER, // ES2017 + SWITCH, // ES2017 + THIS, // ES2017 + THROW, // ES2017 + TRUE, // ES + TRY, // ES2017 + TYPE, // AS possible identifier + TYPEOF, // ES2017 + VAR, // ES2017 + VOID, // ES2017 + WHILE, // ES2017 + WITH, // ES2017 + YIELD, // ES2017 + + // punctuation + + OPENBRACE, + CLOSEBRACE, + OPENPAREN, + CLOSEPAREN, + OPENBRACKET, + CLOSEBRACKET, + DOT, + DOT_DOT_DOT, + SEMICOLON, + COMMA, + LESSTHAN, + GREATERTHAN, + LESSTHAN_EQUALS, + GREATERTHAN_EQUALS, + EQUALS_EQUALS, + EXCLAMATION_EQUALS, + EQUALS_EQUALS_EQUALS, + EXCLAMATION_EQUALS_EQUALS, + EQUALS_GREATERTHAN, + PLUS, + MINUS, + ASTERISK_ASTERISK, + ASTERISK, + SLASH, + PERCENT, + PLUS_PLUS, + MINUS_MINUS, + LESSTHAN_LESSTHAN, + GREATERTHAN_GREATERTHAN, + GREATERTHAN_GREATERTHAN_GREATERTHAN, + AMPERSAND, + BAR, + CARET, + EXCLAMATION, + TILDE, + AMPERSAND_AMPERSAND, + BAR_BAR, + QUESTION, + COLON, + EQUALS, + PLUS_EQUALS, + MINUS_EQUALS, + ASTERISK_EQUALS, + ASTERISK_ASTERISK_EQUALS, + SLASH_EQUALS, + PERCENT_EQUALS, + LESSTHAN_LESSTHAN_EQUALS, + GREATERTHAN_GREATERTHAN_EQUALS, + GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS, + AMPERSAND_EQUALS, + BAR_EQUALS, + CARET_EQUALS, + AT, + + // literals + + IDENTIFIER, + STRINGLITERAL, + INTEGERLITERAL, + FLOATLITERAL, + + // meta + + INVALID, + ENDOFFILE +} + +export function operatorTokenToString(token: Token): string { + switch (token) { + case Token.DELETE: return "delete"; + case Token.IN: return "in"; + case Token.INSTANCEOF: return "instanceof"; + case Token.NEW: return "new"; + case Token.TYPEOF: return "typeof"; + case Token.VOID: return "void"; + case Token.YIELD: return "yield"; + case Token.DOT_DOT_DOT: return "..."; + case Token.COMMA: return ","; + case Token.LESSTHAN: return "<"; + case Token.GREATERTHAN: return ">"; + case Token.LESSTHAN_EQUALS: return "<="; + case Token.GREATERTHAN_EQUALS: return ">="; + case Token.EQUALS_EQUALS: return "=="; + case Token.EXCLAMATION_EQUALS: return "!="; + case Token.EQUALS_EQUALS_EQUALS: return "==="; + case Token.EXCLAMATION_EQUALS_EQUALS: return "!=="; + case Token.PLUS: return "+"; + case Token.MINUS: return "-"; + case Token.ASTERISK_ASTERISK: return "**"; + case Token.ASTERISK: return "*"; + case Token.SLASH: return "/"; + case Token.PERCENT: return "%"; + case Token.PLUS_PLUS: return "++"; + case Token.MINUS_MINUS: return "--"; + case Token.LESSTHAN_LESSTHAN: return "<<"; + case Token.GREATERTHAN_GREATERTHAN: return ">>"; + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: return ">>>"; + case Token.AMPERSAND: return "&"; + case Token.BAR: return "|"; + case Token.CARET: return "^"; + case Token.EXCLAMATION: return "!"; + case Token.TILDE: return "~"; + case Token.AMPERSAND_AMPERSAND: return "&&"; + case Token.BAR_BAR: return "||"; + case Token.EQUALS: return "="; + case Token.PLUS_EQUALS: return "+="; + case Token.MINUS_EQUALS: return "-="; + case Token.ASTERISK_EQUALS: return "*="; + case Token.ASTERISK_ASTERISK_EQUALS: return "**="; + case Token.SLASH_EQUALS: return "/="; + case Token.PERCENT_EQUALS: return "%="; + case Token.LESSTHAN_LESSTHAN_EQUALS: return "<<="; + case Token.GREATERTHAN_GREATERTHAN_EQUALS: return ">>="; + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: return ">>>="; + case Token.AMPERSAND_EQUALS: return "&="; + case Token.BAR_EQUALS: return "|="; + case Token.CARET_EQUALS: return "^="; + default: { + assert(false); + return ""; + } + } +} diff --git a/lib/transformer/src/transformer.ts b/lib/transformer/src/transformer.ts new file mode 100644 index 0000000000..5165f15008 --- /dev/null +++ b/lib/transformer/src/transformer.ts @@ -0,0 +1,21 @@ +import { Parser, Program } from "./compiler"; + +/** + * Top level transformer that expects an afterParse function. + */ +export abstract class Transformer { + constructor( + protected parser: Parser, + protected writeFile: FileWriter, + protected readFile: FileReader, + protected baseDir: string, + protected stdout: (data: string) => void, + protected stderr: (data: string) => void + ) {} + + get program(): Program { + return this.parser.program; + } + + abstract afterParse(): void; +} diff --git a/lib/transformer/src/util.ts b/lib/transformer/src/util.ts new file mode 100644 index 0000000000..6703f1aa12 --- /dev/null +++ b/lib/transformer/src/util.ts @@ -0,0 +1,155 @@ +/** @module util *//***/ + +const indentX1 = " "; +const indentX2 = " "; +const indentX4 = " "; + +/** Creates an indentation matching the number of specified levels. */ +export function indent(sb: string[], level: i32): void { + while (level >= 4) { + sb.push(indentX4); + level -= 4; + } + if (level >= 2) { + sb.push(indentX2); + level -= 2; + } + if (level) { + sb.push(indentX1); + } +} + +/** An enum of named character codes. */ +export const enum CharCode { + + NULL = 0, + LINEFEED = 0x0A, + CARRIAGERETURN = 0x0D, + LINESEPARATOR = 0x2028, + PARAGRAPHSEPARATOR = 0x2029, + NEXTLINE = 0x0085, + + SPACE = 0x20, + NONBREAKINGSPACE = 0xA0, + ENQUAD = 0x2000, + EMQUAD = 0x2001, + ENSPACE = 0x2002, + EMSPACE = 0x2003, + THREEPEREMSPACE = 0x2004, + FOURPEREMSPACE = 0x2005, + SIXPEREMSPACE = 0x2006, + FIGURESPACE = 0x2007, + PUNCTUATIONSPACE = 0x2008, + THINSPACE = 0x2009, + HAIRSPACE = 0x200A, + ZEROWIDTHSPACE = 0x200B, + NARROWNOBREAKSPACE = 0x202F, + IDEOGRAPHICSPACE = 0x3000, + MATHEMATICALSPACE = 0x205F, + OGHAM = 0x1680, + + _ = 0x5F, + + _0 = 0x30, + _1 = 0x31, + _2 = 0x32, + _3 = 0x33, + _4 = 0x34, + _5 = 0x35, + _6 = 0x36, + _7 = 0x37, + _8 = 0x38, + _9 = 0x39, + + a = 0x61, + b = 0x62, + c = 0x63, + d = 0x64, + e = 0x65, + f = 0x66, + g = 0x67, + h = 0x68, + i = 0x69, + j = 0x6A, + k = 0x6B, + l = 0x6C, + m = 0x6D, + n = 0x6E, + o = 0x6F, + p = 0x70, + q = 0x71, + r = 0x72, + s = 0x73, + t = 0x74, + u = 0x75, + v = 0x76, + w = 0x77, + x = 0x78, + y = 0x79, + z = 0x7A, + + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5a, + + AMPERSAND = 0x26, + ASTERISK = 0x2A, + AT = 0x40, + BACKSLASH = 0x5C, + BACKTICK = 0x60, + BAR = 0x7C, + CARET = 0x5E, + CLOSEBRACE = 0x7D, + CLOSEBRACKET = 0x5D, + CLOSEPAREN = 0x29, + COLON = 0x3A, + COMMA = 0x2C, + DOLLAR = 0x24, + DOT = 0x2E, + DOUBLEQUOTE = 0x22, + EQUALS = 0x3D, + EXCLAMATION = 0x21, + GREATERTHAN = 0x3E, + HASH = 0x23, + LESSTHAN = 0x3C, + MINUS = 0x2D, + OPENBRACE = 0x7B, + OPENBRACKET = 0x5B, + OPENPAREN = 0x28, + PERCENT = 0x25, + PLUS = 0x2B, + QUESTION = 0x3F, + SEMICOLON = 0x3B, + SINGLEQUOTE = 0x27, + SLASH = 0x2F, + TILDE = 0x7E, + + BACKSPACE = 0x08, + FORMFEED = 0x0C, + BYTEORDERMARK = 0xFEFF, + TAB = 0x09, + VERTICALTAB = 0x0B +} diff --git a/lib/transformer/src/visitor.ts b/lib/transformer/src/visitor.ts new file mode 100644 index 0000000000..ed25164ce9 --- /dev/null +++ b/lib/transformer/src/visitor.ts @@ -0,0 +1,35 @@ + +export type Collection = T | T[] | Map> | Iterable; + +const isIterable = (object: object): boolean => + //@ts-ignore + object != null && typeof object[Symbol.iterator] === "function"; + + /** + * Top level visitor that will expect an implemented _visit function to visit + * a single node and then provides a generic function for collections of nodes + * and will visit each member of the collection. + */ +export abstract class AbstractVisitor { + visit(node: Collection | null): void { + if (node == null) return; + if (node instanceof Array) { + node.map((node: T): void => { this.visit(node); }); + } else if (node instanceof Map) { + for (let [key, _node] of node.entries()) { + this.visit(_node); + } + //@ts-ignore Need a better way to test type + } else if (isIterable(node)) { + //TODO: Find better way to test if iterable + for (let _node of node) { + this.visit(_node); + } + } else { + //@ts-ignore Node is not iterable. + this._visit(node); + } + } + + protected abstract _visit(node: T): void; +} diff --git a/lib/transformer/src/webpack.d.ts b/lib/transformer/src/webpack.d.ts new file mode 100644 index 0000000000..b4789ec1d3 --- /dev/null +++ b/lib/transformer/src/webpack.d.ts @@ -0,0 +1,4 @@ +declare var DEV: boolean; +//@ts-ignore +declare var globalScope: any; + diff --git a/lib/transformer/tests/assembly/funcs.ts b/lib/transformer/tests/assembly/funcs.ts new file mode 100644 index 0000000000..71ad0b0508 --- /dev/null +++ b/lib/transformer/tests/assembly/funcs.ts @@ -0,0 +1,5 @@ +function Foo(x: i32): void {} + +class FooClass { + Foo(x: i32): void {} +} \ No newline at end of file diff --git a/lib/transformer/tests/assembly/index.ts b/lib/transformer/tests/assembly/index.ts new file mode 100644 index 0000000000..0f346c1d95 --- /dev/null +++ b/lib/transformer/tests/assembly/index.ts @@ -0,0 +1,21 @@ +export class FooBar { + foo: i32 = 0; + bar: u32 = 1; + u64Val: u64 = 4294967297; + u64_zero: u64; + i64Val: i64 = -64; + flag: bool; + private _baz: string = "123"; + uint8array: Uint8Array; + arr: Array>; + u32Arr: u32[]; + i32Arr: i32[]; + // u128Val: u128; + uint8arrays: Array; + // TODO: Fix u64 arrays + u64Arr: u64[]; + + get baz(): string { + return this._baz; + } +} diff --git a/lib/transformer/tests/assembly/tsconfig.json b/lib/transformer/tests/assembly/tsconfig.json new file mode 100644 index 0000000000..7f17010aa6 --- /dev/null +++ b/lib/transformer/tests/assembly/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../../std/assembly.json", + "include": [ + "**/*.ts" + ] +} \ No newline at end of file diff --git a/lib/transformer/tests/package.json b/lib/transformer/tests/package.json new file mode 100644 index 0000000000..0d2aae1c7b --- /dev/null +++ b/lib/transformer/tests/package.json @@ -0,0 +1,10 @@ +{ + "name": "near-bindgen", + "version": "0.0.0", + "description": "Tests for near bindings", + "main": "test.js", + "scripts": { + "build": "../../../bin/asc assembly/index.ts --transform ../dist/ASTPrinter.js --runtime none --noEmit", + "test": "npm run build && node ./test.js && echo \"PASSED!\"" + } +} diff --git a/lib/transformer/tests/test.js b/lib/transformer/tests/test.js new file mode 100644 index 0000000000..9eec79323d --- /dev/null +++ b/lib/transformer/tests/test.js @@ -0,0 +1,51 @@ + + +let asc = require("../../../cli/asc"); + +async function compile(file, transformer) { + + return new Promise( (resolve, reject) => { + const stdout = asc.createMemoryStream(); + const data = {}; + const writeFile = (name,contents) => data[name] = contents; + asc.main([file, "--runtime", "none", "--transform",`../dist/${transformer}.js`, '--textFile', "out.wat"],{stdout, writeFile}, error => { + if (error != null) { + reject(error); + } + resolve({stdout, data}) + }) + }); +} + +function compileString(str, transformer) { + return asc.compileString({"assembly/index.ts": str}, {"runtime": "none", "transform": `../dist/${transformer}.js`}); +} + +function trim(line){ + return line.trim(); +} + +(async function() { + + var result = (await compile("./assembly/index.ts", "ASTPrinter")); + const source = result.stdout.toString(); + const sourceStripped = source.substring(source.indexOf("\n")); + // console.log(result) + debugger; + var actual = compileString(sourceStripped, "ASTPrinter") + const ActualWat = actual.text.substring(); + const ExpectedWat = result.data["out.wat"]; + + var expectedLines = ExpectedWat.split("\n").map(trim); + var actualLines = ActualWat.split("\n").map(trim); + + for (let i = 0; i < actualLines.length; i++){ + if (!expectedLines.includes(actualLines[i])){ + throw new Error("actual line " + i + ": " + actualLines[i] + " is not found in the expected"); + } + } + +})().catch(e => { + console.error('Error during test execution:', e); + process.exit(1); +}); diff --git a/lib/transformer/tsconfig.json b/lib/transformer/tsconfig.json new file mode 100644 index 0000000000..bd0fdca494 --- /dev/null +++ b/lib/transformer/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "noLib": true, + "inlineSourceMap": true, + "inlineSources": true, + "target": "es5", + "module": "commonjs", + "allowJs": true, + "downlevelIteration": true, + "preserveConstEnums": true, + "alwaysStrict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noEmitOnError": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "typeRoots": ["./types"] + // "types": ["portable", ] + }, + "include": ["src/**/*.ts"] +} + \ No newline at end of file diff --git a/lib/transformer/types/common/index.d.ts b/lib/transformer/types/common/index.d.ts new file mode 100644 index 0000000000..9c17c843dc --- /dev/null +++ b/lib/transformer/types/common/index.d.ts @@ -0,0 +1,429 @@ + + +/** Path delimiter inserted between file system levels. */ +declare const PATH_DELIMITER = "/"; +/** Substitution used to indicate the parent directory. */ +declare const PARENT_SUBST = ".."; +/** Function name prefix used for getters. */ +declare const GETTER_PREFIX = "get:"; +/** Function name prefix used for setters. */ +declare const SETTER_PREFIX = "set:"; +/** Delimiter used between class names and instance members. */ +declare const INSTANCE_DELIMITER = "#"; +/** Delimiter used between class and namespace names and static members. */ +declare const STATIC_DELIMITER = "."; +/** Delimiter used between a function and its inner elements. */ +declare const INNER_DELIMITER = "~"; +/** Substitution used to indicate a library directory. */ +declare const LIBRARY_SUBST: "~lib"; +/** Library directory prefix. */ +declare const LIBRARY_PREFIX: string; // = LIBRARY_SUBST + PATH_DELIMITER; +/** Path index suffix. */ +declare const INDEX_SUFFIX: string; // = PATH_DELIMITER + "index"; +declare namespace CommonSymbols { + const i8 = "i8"; + /** Common compiler symbols. */ + // special + const EMPTY = ""; + // types + + const i16 = "i16"; + const i32 = "i32"; + const i64 = "i64"; + const isize = "isize"; + const u8 = "u8"; + const u16 = "u16"; + const u32 = "u32"; + const u64 = "u64"; + const usize = "usize"; + const bool = "bool"; + const f32 = "f32"; + const f64 = "f64"; + const v128 = "v128"; + const anyref = "anyref"; + const i8x16 = "i8x16"; + const u8x16 = "u8x16"; + const i16x8 = "i16x8"; + const u16x8 = "u16x8"; + const i32x4 = "i32x4"; + const u32x4 = "u32x4"; + const i64x2 = "i64x2"; + const u64x2 = "u64x2"; + const f32x4 = "f32x4"; + const f64x2 = "f64x2"; + const void_ = "void"; + const number = "number"; + const boolean = "boolean"; + const string = "string"; + const native = "native"; + const indexof = "indexof"; + const valueof = "valueof"; + const returnof = "returnof"; + // aliases + const null_ = "null"; + const true_ = "true"; + const false_ = "false"; + // objects + const this_ = "this"; + const super_ = "super"; + const constructor = "constructor"; + // constants + const ASC_TARGET = "ASC_TARGET"; + const ASC_NO_TREESHAKING = "ASC_NO_TREESHAKING"; + const ASC_NO_ASSERT = "ASC_NO_ASSERT"; + const ASC_MEMORY_BASE = "ASC_MEMORY_BASE"; + const ASC_OPTIMIZE_LEVEL = "ASC_OPTIMIZE_LEVEL"; + const ASC_SHRINK_LEVEL = "ASC_SHRINK_LEVEL"; + const ASC_FEATURE_SIGN_EXTENSION = "ASC_FEATURE_SIGN_EXTENSION"; + const ASC_FEATURE_MUTABLE_GLOBALS = "ASC_FEATURE_MUTABLE_GLOBALS"; + const ASC_FEATURE_NONTRAPPING_F2I = "ASC_FEATURE_NONTRAPPING_F2I"; + const ASC_FEATURE_BULK_MEMORY = "ASC_FEATURE_BULK_MEMORY"; + const ASC_FEATURE_SIMD = "ASC_FEATURE_SIMD"; + const ASC_FEATURE_THREADS = "ASC_FEATURE_THREADS"; + const ASC_FEATURE_EXCEPTION_HANDLING = "ASC_FEATURE_EXCEPTION_HANDLING"; + const ASC_FEATURE_TAIL_CALLS = "ASC_FEATURE_TAIL_CALLS"; + const ASC_FEATURE_REFERENCE_TYPES = "ASC_FEATURE_REFERENCE_TYPES"; + // classes + const I8 = "I8"; + const I16 = "I16"; + const I32 = "I32"; + const I64 = "I64"; + const Isize = "Isize"; + const U8 = "U8"; + const U16 = "U16"; + const U32 = "U32"; + const U64 = "U64"; + const Usize = "Usize"; + const Bool = "Bool"; + const F32 = "F32"; + const F64 = "F64"; + const V128 = "V128"; + const Anyref = "Anyref"; + const String = "String"; + const Array = "Array"; + const FixedArray = "FixedArray"; + const Set = "Set"; + const Map = "Map"; + const ArrayBufferView = "ArrayBufferView"; + const ArrayBuffer = "ArrayBuffer"; + const Math = "Math"; + const Mathf = "Mathf"; + const Int8Array = "Int8Array"; + const Int16Array = "Int16Array"; + const Int32Array = "Int32Array"; + const Int64Array = "Int64Array"; + const Uint8Array = "Uint8Array"; + const Uint8ClampedArray = "Uint8ClampedArray"; + const Uint16Array = "Uint16Array"; + const Uint32Array = "Uint32Array"; + const Uint64Array = "Uint64Array"; + const Float32Array = "Float32Array"; + const Float64Array = "Float64Array"; + // runtime + const abort = "abort"; + const pow = "pow"; + const mod = "mod"; + const __alloc = "__alloc"; + const __realloc = "__realloc"; + const __free = "__free"; + const __retain = "__retain"; + const __release = "__release"; + const __collect = "__collect"; + const __typeinfo = "__typeinfo"; + const __instanceof = "__instanceof"; + const __visit = "__visit"; + const __allocArray = "__allocArray"; +} + +declare type CommonSymbols = +// special types +"EMPTY" +| "i16" +| "i32" +| "i64" +| "isize" +| "u8" +| "u16" +| "u32" +| "u64" +| "usize" +| "bool" +| "f32" +| "f64" +| "v128" +| "anyref" +| "i8x16" +| "u8x16" +| "i16x8" +| "u16x8" +| "i32x4" +| "u32x4" +| "i64x2" +| "u64x2" +| "f32x4" +| "f64x2" +| "void" +| "number" +| "boolean" +| "string" +| "native" +| "indexof" +| "valueof" +| "returnof" +// aliases +| "null" +| "true" +| "false" +// objects +| "this" +| "super" +| "constructor" +// constants +| "ASC_TARGET" +| "ASC_NO_TREESHAKING" +| "ASC_NO_ASSERT" +| "ASC_MEMORY_BASE" +| "ASC_OPTIMIZE_LEVEL" +| "ASC_SHRINK_LEVEL" +| "ASC_FEATURE_SIGN_EXTENSION" +| "ASC_FEATURE_MUTABLE_GLOBALS" +| "ASC_FEATURE_NONTRAPPING_F2I" +| "ASC_FEATURE_BULK_MEMORY" +| "ASC_FEATURE_SIMD" +| "ASC_FEATURE_THREADS" +| "ASC_FEATURE_EXCEPTION_HANDLING" +| "ASC_FEATURE_TAIL_CALLS" +| "ASC_FEATURE_REFERENCE_TYPES" +// classes +| "I8" +| "I16" +| "I32" +| "I64" +| "Isize" +| "U8" +| "U16" +| "U32" +| "U64" +| "Usize" +| "Bool" +| "F32" +| "F64" +| "V128" +| "Anyref" +| "String" +| "Array" +| "FixedArray" +| "Set" +| "Map" +| "ArrayBufferView" +| "ArrayBuffer" +| "Math" +| "Mathf" +| "Int8Array" +| "Int16Array" +| "Int32Array" +| "Int64Array" +| "Uint8Array" +| "Uint8ClampedArray" +| "Uint16Array" +| "Uint32Array" +| "Uint64Array" +| "Float32Array" +| "Float64Array" +// runtime +| "abort" +| "pow" +| "mod" +| "__alloc" +| "__realloc" +| "__free" +| "__retain" +| "__release" +| "__collect" +| "__typeinfo" +| "__instanceof" +| "__visit" +| "__allocArray" + +/** An enum of named character codes. */ +declare enum CharCode { + + NULL = 0, + LINEFEED = 0x0A, + CARRIAGERETURN = 0x0D, + LINESEPARATOR = 0x2028, + PARAGRAPHSEPARATOR = 0x2029, + NEXTLINE = 0x0085, + + SPACE = 0x20, + NONBREAKINGSPACE = 0xA0, + ENQUAD = 0x2000, + EMQUAD = 0x2001, + ENSPACE = 0x2002, + EMSPACE = 0x2003, + THREEPEREMSPACE = 0x2004, + FOURPEREMSPACE = 0x2005, + SIXPEREMSPACE = 0x2006, + FIGURESPACE = 0x2007, + PUNCTUATIONSPACE = 0x2008, + THINSPACE = 0x2009, + HAIRSPACE = 0x200A, + ZEROWIDTHSPACE = 0x200B, + NARROWNOBREAKSPACE = 0x202F, + IDEOGRAPHICSPACE = 0x3000, + MATHEMATICALSPACE = 0x205F, + OGHAM = 0x1680, + + _ = 0x5F, + + _0 = 0x30, + _1 = 0x31, + _2 = 0x32, + _3 = 0x33, + _4 = 0x34, + _5 = 0x35, + _6 = 0x36, + _7 = 0x37, + _8 = 0x38, + _9 = 0x39, + + a = 0x61, + b = 0x62, + c = 0x63, + d = 0x64, + e = 0x65, + f = 0x66, + g = 0x67, + h = 0x68, + i = 0x69, + j = 0x6A, + k = 0x6B, + l = 0x6C, + m = 0x6D, + n = 0x6E, + o = 0x6F, + p = 0x70, + q = 0x71, + r = 0x72, + s = 0x73, + t = 0x74, + u = 0x75, + v = 0x76, + w = 0x77, + x = 0x78, + y = 0x79, + z = 0x7A, + + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5a, + + AMPERSAND = 0x26, + ASTERISK = 0x2A, + AT = 0x40, + BACKSLASH = 0x5C, + BACKTICK = 0x60, + BAR = 0x7C, + CARET = 0x5E, + CLOSEBRACE = 0x7D, + CLOSEBRACKET = 0x5D, + CLOSEPAREN = 0x29, + COLON = 0x3A, + COMMA = 0x2C, + DOLLAR = 0x24, + DOT = 0x2E, + DOUBLEQUOTE = 0x22, + EQUALS = 0x3D, + EXCLAMATION = 0x21, + GREATERTHAN = 0x3E, + HASH = 0x23, + LESSTHAN = 0x3C, + MINUS = 0x2D, + OPENBRACE = 0x7B, + OPENBRACKET = 0x5B, + OPENPAREN = 0x28, + PERCENT = 0x25, + PLUS = 0x2B, + QUESTION = 0x3F, + SEMICOLON = 0x3B, + SINGLEQUOTE = 0x27, + SLASH = 0x2F, + TILDE = 0x7E, + + BACKSPACE = 0x08, + FORMFEED = 0x0C, + BYTEORDERMARK = 0xFEFF, + TAB = 0x09, + VERTICALTAB = 0x0B +} + +/** @module glue/js *//***/ + +declare type I64 = { __Long__: true }; // opaque + +declare const i64_zero: I64; +declare const i64_one: I64; + +declare function i64_new(lo: i32, hi?: i32): I64; +declare function i64_low(value: I64): i32; +declare function i64_high(value: I64): i32; + +declare function i64_add(left: I64, right: I64): I64; +declare function i64_sub(left: I64, right: I64): I64; +declare function i64_mul(left: I64, right: I64): I64; +declare function i64_div(left: I64, right: I64): I64; +declare function i64_div_u(left: I64, right: I64): I64; +declare function i64_rem(left: I64, right: I64): I64; +declare function i64_rem_u(left: I64, right: I64): I64; +declare function i64_and(left: I64, right: I64): I64; +declare function i64_or(left: I64, right: I64): I64; +declare function i64_xor(left: I64, right: I64): I64; +declare function i64_shl(left: I64, right: I64): I64; +declare function i64_shr(left: I64, right: I64): I64; +declare function i64_shr_u(left: I64, right: I64): I64; +declare function i64_not(value: I64): I64; + +declare function i64_eq(left: I64, right: I64): bool; +declare function i64_ne(left: I64, right: I64): bool; + +declare function i64_align(value: I64, alignment: i32): I64; + +declare function i64_is_i8(value: I64): bool; +declare function i64_is_i16(value: I64): bool; +declare function i64_is_i32(value: I64): bool; +declare function i64_is_u8(value: I64): bool; +declare function i64_is_u16(value: I64): bool; +declare function i64_is_u32(value: I64): bool; +declare function i64_is_bool(value: I64): bool; +declare function i64_is_f32(value: I64): bool; +declare function i64_is_f64(value: I64): bool; + +declare function i64_to_f32(value: I64): f64; +declare function i64_to_f64(value: I64): f64; +declare function i64_to_string(value: I64, unsigned?: bool): string; + +declare type FileWriter = (filename: string, contents: Uint8Array | string, baseDir: string) => void +declare type FileReader = (filename: string, baseDir?: string) => string | null diff --git a/lib/transformer/types/index.d.ts b/lib/transformer/types/index.d.ts new file mode 100644 index 0000000000..333046c81e --- /dev/null +++ b/lib/transformer/types/index.d.ts @@ -0,0 +1,2 @@ +import "./portable"; +import "./common"; \ No newline at end of file diff --git a/lib/transformer/types/portable/index.d.ts b/lib/transformer/types/portable/index.d.ts new file mode 100644 index 0000000000..7fd2778477 --- /dev/null +++ b/lib/transformer/types/portable/index.d.ts @@ -0,0 +1,5 @@ +import "../../../../std/types/portable"; +import "../../../src/glue/js/i64"; +import "../../../src/glue/js/float"; + + diff --git a/lib/transformer/types/tsconfig.json b/lib/transformer/types/tsconfig.json new file mode 100644 index 0000000000..36b081f2cc --- /dev/null +++ b/lib/transformer/types/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "noLib": true, + "inlineSourceMap": true, + "inlineSources": true, + "target": "es5", + "module": "commonjs", + "allowJs": true, + "downlevelIteration": true, + "preserveConstEnums": true, + "alwaysStrict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noEmitOnError": true, + "strictNullChecks": true, + "experimentalDecorators": true + + }, + "include": ["**/*.ts"] +} \ No newline at end of file diff --git a/lib/transformer/webpack.base.js b/lib/transformer/webpack.base.js new file mode 100644 index 0000000000..b7e96f1031 --- /dev/null +++ b/lib/transformer/webpack.base.js @@ -0,0 +1,64 @@ + +const path = require("path"); +const fs = require("fs"); + +// Creates the webpack config +// Need to provide a local installation of webpack since external packages will use this +// files: object of name: path, for each transformer +// outfolder: where to put the generated files +// toBundle: list of paths of files that will be bundled and available +// to the transformer via the `BUNDLE` global variable. +function _config(webpack, files, outfolder, toBundle) { + const config = { + entry: files, + module: { + rules: [ + { + test: /\.ts$/, + loader: "ts-loader", + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: [ ".ts", ".js" ] + }, + output: { + filename: "[name].js", + path: outfolder, + library: "transformer", + libraryTarget: "umd", + globalObject: "typeof self !== 'undefined' ? self : this" + }, + node: { + fs: 'empty' + } + }; + return (env, argv) => { + let dev = false; + if (argv.mode == "development") { + config.devtool = 'source-map'; + dev = true; + } else { + argv.mode = "production"; + } + config.plugins = [ + new webpack.DefinePlugin({ + DEV: dev, + BUNDLE: (() => { + if (toBundle){ + const lib = {}; + toBundle.forEach(file => lib[path.basename(file).replace(/\.ts$/, "")] = bundleFile(file)); + return lib; + } + })(), + }) + ] + return config; + } +} +function bundleFile(filename) { + return JSON.stringify(fs.readFileSync(filename, { encoding: "utf8" }).replace(/\r\n/g, "\n")); +} + +module.exports = _config; \ No newline at end of file diff --git a/lib/transformer/webpack.config.js b/lib/transformer/webpack.config.js new file mode 100644 index 0000000000..40feaac782 --- /dev/null +++ b/lib/transformer/webpack.config.js @@ -0,0 +1,13 @@ +const path = require("path"); +const webpack = require("webpack"); +const config = require("./webpack.base"); + +const files = { + ASTPrinter: "./src/examples/printer.ts", + FuncVisitor: "./src/examples/functions.ts" + }; + +const outfolder = path.resolve(__dirname, "dist"); + + +module.exports = config(webpack, files, outfolder); diff --git a/package.json b/package.json index d008faa551..ddb0fd58b8 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,11 @@ "check": "npm run check:config && npm run check:compiler", "check:config": "tsc --noEmit -p src --diagnostics --listFiles", "check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as", - "test": "npm run test:parser && npm run test:compiler && npm run test:packages", + "test": "npm run test:parser && npm run test:compiler && npm run test:packages && npm run test:transformer", "test:parser": "node tests/parser", "test:compiler": "node tests/compiler", "test:packages": "cd tests/packages && npm run test", + "test:transformer": "cd lib/transformer && npm run test", "make": "npm run clean && npm test && npm run build && npm test", "all": "npm run check && npm run make", "docs": "typedoc --tsconfig tsconfig-docs.json --mode modules --name \"AssemblyScript Compiler API\" --out ./docs/api --ignoreCompilerErrors --excludeNotExported --excludePrivate --excludeExternals --exclude **/std/** --includeDeclarations --readme src/README.md", @@ -65,6 +66,11 @@ "lib/loader/index.d.ts", "lib/loader/index.js", "lib/loader/README.md", + "lib/transformer/dist/", + "lib/transformer/types/", + "lib/transformer/tsconfig.json", + "lib/transformer/src/", + "lib/transformer/webpack.base.js", "bin/", "cli/", "dist/", diff --git a/std/portable/index.js b/std/portable/index.js index 7241652149..9170f333e0 100644 --- a/std/portable/index.js +++ b/std/portable/index.js @@ -259,21 +259,25 @@ globalScope["fmodf"] = function fmodf(x, y) { globalScope["JSMath"] = Math; -Object.defineProperties(globalScope["JSMath"], { - sincos_sin: { value: 0.0, writable: true }, - sincos_cos: { value: 0.0, writable: true }, - signbit: { - value: function signbit(x) { - F64[0] = x; return Boolean((U64[1] >>> 31) & (x == x)); - } - }, - sincos: { - value: function sincos(x) { - this.sincos_sin = Math.sin(x); - this.sincos_cos = Math.cos(x); +try { + Object.defineProperties(globalScope["JSMath"], { + sincos_sin: { value: 0.0, writable: true }, + sincos_cos: { value: 0.0, writable: true }, + signbit: { + value: function signbit(x) { + F64[0] = x; return Boolean((U64[1] >>> 31) & (x == x)); + } + }, + sincos: { + value: function sincos(x) { + this.sincos_sin = Math.sin(x); + this.sincos_cos = Math.cos(x); + } } - } -}); + }); +} catch (e) { + +} globalScope["memory"] = (() => { var HEAP = new Uint8Array(0);