Created
April 19, 2019 19:06
-
-
Save rogerpoon/b7b63aecaf8e283172886aa98547fef2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class Node {} | |
abstract class Statement : Node {} | |
abstract class Expression : Node {} | |
class Program : Node | |
{ | |
Statement[] body; | |
Program() { | |
this.body = []; | |
} | |
Program(...Statement body) { | |
this.body = body; | |
} | |
void push(...Statement statements) { | |
body = body.concat(statements); | |
} | |
override string toString() { | |
return "!function(){%s}()".format(body.join("")); | |
} | |
} | |
class Identifier : Expression | |
{ | |
string name; | |
Identifier(string name) { | |
this.name = name; | |
} | |
override string toString() { | |
return name.toString(); | |
} | |
} | |
class BlockStatement : Statement | |
{ | |
Statement[] body; | |
BlockStatement() { | |
this.body = []; | |
} | |
BlockStatement(...Statement body) { | |
this.body = body; | |
} | |
override string toString() { | |
return "{" + body.join("") + "}"; | |
} | |
} | |
class EmptyStatement : Statement | |
{ | |
override string toString() { | |
return ";"; | |
} | |
} | |
class ExpressionStatement : Statement | |
{ | |
Expression expression; | |
ExpressionStatement(Expression expression) { | |
this.expression = expression; | |
} | |
override string toString() { | |
return expression.toString() + ";"; | |
} | |
} | |
class IfStatement : Statement | |
{ | |
Expression condition; | |
Statement consequent; | |
Statement? alternate; | |
IfStatement(Expression condition, Statement consequent) { | |
this.condition = condition; | |
this.consequent = consequent; | |
this.alternate = null; | |
} | |
IfStatement(Expression condition, | |
Statement consequent, | |
Statement alternate) | |
{ | |
this.condition = condition; | |
this.consequent = consequent; | |
this.alternate = alternate; | |
} | |
override string toString() { | |
if (alternate == null) { | |
return "if(%s)%s".format(condition.toString(), consequent.toString()); | |
} | |
else { | |
return "if(%s)%selse %s".format( condition.toString(), | |
consequent.toString(), | |
(string) alternate?.toString()); | |
} | |
} | |
} | |
class LabeledStatement : Statement | |
{ | |
Identifier label; | |
Statement body; | |
LabeledStatement(Identifier label, Statement body) { | |
this.label = label; | |
this.body = body; | |
} | |
override string toString() { | |
return label.toString() + ":" + body.toString(); | |
} | |
} | |
class BreakStatement : Statement | |
{ | |
Identifier? label; | |
BreakStatement() { | |
this.label = null; | |
} | |
BreakStatement(Identifier label) { | |
this.label = label; | |
} | |
override string toString() { | |
return "break%s;".format(label?.toString()?.prepend(" ") ?? ""); | |
} | |
} | |
class ContinueStatement : Statement | |
{ | |
Identifier? label; | |
ContinueStatement() { | |
this.label = null; | |
} | |
ContinueStatement(Identifier label) { | |
this.label = label; | |
} | |
override string toString() { | |
return "continue%s;".format(label?.toString()?.prepend(" ") ?? ""); | |
} | |
} | |
class WithStatement : Statement | |
{ | |
Expression object; | |
Statement body; | |
WithStatement(Expression object, Statement body) { | |
this.object = object; | |
this.body = body; | |
} | |
override string toString() { | |
return "with(%s)%s".format(object.toString(), body.toString()); | |
} | |
} | |
abstract class SwitchCondition : Node {} | |
class SwitchCase : SwitchCondition | |
{ | |
Expression test; | |
Statement[] consequents; | |
SwitchCase(Expression test, ...Statement consequents) { | |
this.test = test; | |
this.consequents = consequents; | |
} | |
override string toString() { | |
return "case " + test.toString() + ":" + consequents.toString(); | |
} | |
} | |
class SwitchDefault : SwitchCondition | |
{ | |
Statement[] consequents; | |
SwitchDefault(...Statement consequents) { | |
this.consequents = consequents; | |
} | |
override string toString() { | |
return "default:" + consequents.toString(); | |
} | |
} | |
class SwitchStatement : Statement | |
{ | |
Expression discriminant; | |
SwitchCondition[] cases; | |
SwitchStatement(Expression discriminant, ...SwitchCondition cases) { | |
this.discriminant = discriminant; | |
this.cases = cases; | |
} | |
override string toString() { | |
return "switch(%s){%s}".format(discriminant.toString(), cases.join("")); | |
} | |
} | |
class ReturnStatement : Statement | |
{ | |
Expression? argument; | |
ReturnStatement() { | |
this.argument = null; | |
} | |
ReturnStatement(Expression argument) { | |
this.argument = argument; | |
} | |
override string toString() { | |
return "return%s;".format(argument?.toString()?.prepend(" ") ?? ""); | |
} | |
} | |
class ThrowStatement : Statement | |
{ | |
Expression argument; | |
ThrowStatement(Expression argument) { | |
this.argument = argument; | |
} | |
override string toString() { | |
return "throw " + argument.toString() + ";"; | |
} | |
} | |
class CatchClause : Node | |
{ | |
Expression param; | |
BlockStatement body; | |
CatchClause(Expression param, BlockStatement body) { | |
this.param = param; | |
this.body = body; | |
} | |
override string toString() { | |
return "catch(" + param.toString() + ")" + body.toString(); | |
} | |
} | |
class TryStatement : Statement | |
{ | |
BlockStatement block; | |
CatchClause handler; | |
BlockStatement? finalizer; | |
TryStatement(BlockStatement block, CatchClause handler) { | |
this.block = block; | |
this.handler = handler; | |
this.finalizer = null; | |
} | |
TryStatement(BlockStatement block, CatchClause handler, BlockStatement finalizer) { | |
this.block = block; | |
this.handler = handler; | |
this.finalizer = finalizer; | |
} | |
override string toString() { | |
if (finalizer == null) { | |
return "try" + block.toString() + handler.toString(); | |
} | |
else { | |
return "try%s%sfinally%s".format(block.toString(), handler.toString(), (string) finalizer?.toString()); | |
} | |
} | |
} | |
class WhileStatement : Statement | |
{ | |
Expression test; | |
Statement body; | |
WhileStatement(Expression test, Statement body) { | |
this.test = test; | |
this.body = body; | |
} | |
override string toString() { | |
return "while(%s)%s".format(test.toString(), body.toString()); | |
} | |
} | |
class DoWhileStatement : Statement | |
{ | |
Statement body; | |
Expression test; | |
DoWhileStatement(Statement body, Expression test) { | |
this.body = body; | |
this.test = test; | |
} | |
override string toString() { | |
return "do %s while(%s);".format(body.toString(), test.toString()); | |
} | |
} | |
class ForStatement : Statement | |
{ | |
Node? init; | |
Expression? test; | |
Expression? update; | |
Statement body; | |
ForStatement(VariableDeclaration? init, Expression? test, Expression? update, Statement body) { | |
this.init = init; | |
this.test = test; | |
this.update = update; | |
this.body = body; | |
} | |
ForStatement(Expression? init, Expression? test, Expression? update, Statement body) { | |
this.init = init; | |
this.test = test; | |
this.update = update; | |
this.body = body; | |
} | |
override string toString() { | |
return "for(%s;%s;%s)%s".format(init?.toString() ?? "", | |
test?.toString() ?? "", | |
update?.toString() ?? "", | |
body.toString()); | |
} | |
} | |
class ForInStatement : Statement | |
{ | |
Node left; | |
Expression right; | |
Statement body; | |
ForInStatement(VariableDeclaration left, Expression right, Statement body) { | |
this.left = left; | |
this.right = right; | |
this.body = body; | |
} | |
ForInStatement(Identifier left, Expression right, Statement body) { | |
this.left = left; | |
this.right = right; | |
this.body = body; | |
} | |
override string toString() { | |
return "for(%s in %s)%s".format(left.toString(), right.toString(), body.toString()); | |
} | |
} | |
class DebuggerStatement : Statement | |
{ | |
override string toString() { | |
return "debugger;"; | |
} | |
} | |
class FunctionDeclaration : Statement | |
{ | |
Identifier id; | |
Identifier[] params; | |
BlockStatement body; | |
FunctionDeclaration(Identifier id, ...Identifier params, BlockStatement body) { | |
this.id = id; | |
this.params = params; | |
this.body = body; | |
} | |
override string toString() { | |
return "function %s(%s)%s".format(id.toString(), params.join(","), body.toString()); | |
} | |
} | |
class VariableDeclarator : Node | |
{ | |
Identifier id; | |
Expression? init; | |
VariableDeclarator(Identifier id) { | |
this.id = id; | |
this.init = null; | |
} | |
VariableDeclarator(Identifier id, Expression init) { | |
this.id = id; | |
this.init = init; | |
} | |
override string toString() { | |
if (init == null) { | |
return id.toString(); | |
} | |
else { | |
return id.toString() + "=" + (string) init?.toString(); | |
} | |
} | |
} | |
class VariableDeclaration : Node | |
{ | |
VariableDeclarator[] declarations; | |
VariableDeclaration(...VariableDeclarator declarations) { | |
this.declarations = declarations; | |
} | |
override string toString() { | |
return "var " + declarations.join(","); | |
} | |
} | |
class VariableStatement : Statement | |
{ | |
VariableDeclaration declaration; | |
VariableStatement(VariableDeclaration declaration) { | |
this.declaration = declaration; | |
} | |
override string toString() { | |
return declaration.toString() + ";"; | |
} | |
} | |
class ThisExpression : Expression | |
{ | |
override string toString() { | |
return "this"; | |
} | |
} | |
class ArrayExpression : Expression | |
{ | |
Expression[] elements; | |
ArrayExpression() { | |
this.elements = []; | |
} | |
ArrayExpression(...Expression elements) { | |
this.elements = elements; | |
} | |
ArrayExpression(descend Expression[] elements) { | |
this.elements = []; | |
foreach(Expression e in elements) { | |
this.elements.push(e); | |
} | |
} | |
override string toString() { | |
return "[" + elements.join(",") + "]"; | |
} | |
} | |
class ObjectProperty : Node | |
{ | |
Expression key; | |
Expression value; | |
ObjectProperty(Identifier key, Expression value) { | |
this.key = key; | |
this.value = value; | |
} | |
ObjectProperty(StringLiteral key, Expression value) { | |
this.key = key; | |
this.value = value; | |
} | |
override string toString() { | |
return key.toString() + ":" + value.toString(); | |
} | |
} | |
class ObjectExpression : Expression | |
{ | |
ObjectProperty[] properties; | |
ObjectExpression(...ObjectProperty properties) { | |
this.properties = properties; | |
} | |
override string toString() { | |
return "{" + properties.join(",") + "}"; | |
} | |
} | |
class FunctionExpression : Expression | |
{ | |
Identifier? id; | |
Identifier[] params; | |
BlockStatement body; | |
FunctionExpression(...Identifier params, BlockStatement body) { | |
this.id = null; | |
this.params = params; | |
this.body = body; | |
} | |
FunctionExpression(Identifier id, ...Identifier params, BlockStatement body) { | |
this.id = id; | |
this.params = params; | |
this.body = body; | |
} | |
bool isAnonymous() { | |
return this.id == null; | |
} | |
override string toString() { | |
return "function%s(%s)%s".format(id?.toString()?.prepend(" ") ?? "", params.join(","), body.toString()); | |
} | |
} | |
class GroupExpression : Expression | |
{ | |
Expression expression; | |
GroupExpression(Expression expression) { | |
this.expression = expression; | |
} | |
override string toString() { | |
return "(" + expression.toString() + ")"; | |
} | |
} | |
class SequenceExpression : Expression | |
{ | |
Expression[] expressions; | |
SequenceExpression(...Expression expressions) { | |
this.expressions = expressions; | |
} | |
override string toString() { | |
return expressions.join(","); | |
} | |
} | |
class UnaryExpression : Expression | |
{ | |
string operator; | |
Expression argument; | |
UnaryExpression(string operator, Expression argument) { | |
this.operator = operator; | |
this.argument = argument; | |
} | |
override string toString() { | |
bool isKeyword = operator.isLowerCase(); | |
string spacer = isKeyword ? " " : ""; | |
return operator.toString() + spacer + argument.toString(); | |
} | |
} | |
class BinaryExpression : Expression | |
{ | |
Expression left; | |
string operator; | |
Expression right; | |
BinaryExpression(Expression left, string operator, Expression right) { | |
this.left = left; | |
this.operator = operator; | |
this.right = right; | |
} | |
override string toString() { | |
return left.toString() + operator.toString() + right.toString(); | |
} | |
} | |
class AssignmentExpression : Expression | |
{ | |
Expression left; | |
string? operator; | |
Expression right; | |
AssignmentExpression(Identifier left, Expression right) { | |
this.left = left; | |
this.operator = null; | |
this.right = right; | |
} | |
AssignmentExpression(MemberExpression left, Expression right) { | |
this.left = left; | |
this.operator = null; | |
this.right = right; | |
} | |
AssignmentExpression(AssignmentExpression left, Expression right) { | |
this.left = left; | |
this.operator = null; | |
this.right = right; | |
} | |
AssignmentExpression(Identifier left, string operator, Expression right) { | |
this.left = left; | |
this.operator = operator; | |
this.right = right; | |
} | |
AssignmentExpression(MemberExpression left, string operator, Expression right) { | |
this.left = left; | |
this.operator = operator; | |
this.right = right; | |
} | |
AssignmentExpression(AssignmentExpression left, string operator, Expression right) { | |
this.left = left; | |
this.operator = operator; | |
this.right = right; | |
} | |
override string toString() { | |
string assignment = operator ?? "="; | |
return left.toString() + assignment + right.toString(); | |
} | |
} | |
class UpdateExpression : Expression | |
{ | |
string operator; | |
bool prefix; | |
Expression argument; | |
UpdateExpression(string operator, Expression argument, bool prefix) { | |
this.operator = operator; | |
this.argument = argument; | |
this.prefix = prefix; | |
} | |
override string toString() { | |
if (prefix) { | |
// Prefix with space to avoid generation of `a + ++b` as `a+++b` | |
return operator.prepend(" ") + argument.toString(); | |
} | |
else { | |
// Append space to avoid generation of `a++ + b` as `a+++b` | |
return argument.toString() + operator.append(" "); | |
} | |
} | |
} | |
class LogicalExpression : Expression | |
{ | |
Expression left; | |
string operator; | |
Expression right; | |
LogicalExpression(Expression left, string operator, Expression right) { | |
this.left = left; | |
this.operator = operator; | |
this.right = right; | |
} | |
override string toString() { | |
return left.toString() + operator.toString() + right.toString(); | |
} | |
} | |
class ConditionalExpression : Expression | |
{ | |
Expression test; | |
Expression consequent; | |
Expression alternate; | |
ConditionalExpression(Expression test, Expression consequent, Expression alternate) { | |
this.test = test; | |
this.consequent = consequent; | |
this.alternate = alternate; | |
} | |
override string toString() { | |
return "%s?%s:%s".format(test.toString(), consequent.toString(), alternate.toString()); | |
} | |
} | |
class NewExpression : Expression | |
{ | |
Expression callee; | |
Expression[] arguments; | |
NewExpression(Expression callee) { | |
this.callee = callee; | |
this.arguments = []; | |
} | |
NewExpression(Expression callee, ...Expression arguments) { | |
this.callee = callee; | |
this.arguments = arguments; | |
} | |
override string toString() { | |
return "new %s(%s)".format(callee.toString(), arguments.join(",")); | |
} | |
} | |
class CallExpression : Expression | |
{ | |
Expression callee; | |
Expression[] arguments; | |
CallExpression(Expression callee, ...Expression arguments) { | |
this.callee = callee; | |
this.arguments = arguments; | |
} | |
override string toString() { | |
return "%s(%s)".format(callee.toString(), arguments.join(",")); | |
} | |
} | |
class MemberExpression : Expression | |
{ | |
Expression object; | |
Expression prop; | |
bool computed; | |
MemberExpression(Expression object, Identifier prop) { | |
this.object = object; | |
this.prop = prop; | |
this.computed = false; | |
} | |
MemberExpression(Expression object, Identifier prop, bool computed) { | |
this.object = object; | |
this.prop = prop; | |
this.computed = computed; | |
} | |
MemberExpression(Expression object, Expression prop) { | |
this.object = object; | |
this.prop = prop; | |
this.computed = true; | |
} | |
override string toString() { | |
if (computed) { | |
return "%s[%s]".format(object.toString(), prop.toString()); | |
} | |
else { | |
return object.toString() + "." + prop.toString(); | |
} | |
} | |
} | |
abstract class Literal : Expression {} | |
class NullLiteral : Literal | |
{ | |
override string toString() { | |
return "null"; | |
} | |
} | |
class UndefinedLiteral : Literal | |
{ | |
override string toString() { | |
return "undefined"; | |
} | |
} | |
class StringLiteral : Literal | |
{ | |
string value; | |
StringLiteral(string value) { | |
this.value = value; | |
} | |
override string toString() { | |
return '"' + value.toString() + '"'; | |
} | |
} | |
class NumericLiteral : Literal | |
{ | |
string value; // Use 'string' to deal with precision issues | |
NumericLiteral(string value) { | |
this.value = value; | |
} | |
override string toString() { | |
return value.toString(); | |
} | |
} | |
class RegExpLiteral : Literal | |
{ | |
string pattern; | |
char[] flags; | |
RegExpLiteral(string pattern) { | |
this.pattern = pattern; | |
this.flags = []; | |
} | |
RegExpLiteral(string pattern, char[] flags) { | |
this.pattern = pattern; | |
this.flags = flags; | |
} | |
override string toString() { | |
return "/" + pattern + "/" + flags.join(""); | |
} | |
} | |
class BooleanLiteral : Literal | |
{ | |
bool value; | |
BooleanLiteral(bool value) { | |
this.value = value; | |
} | |
override string toString() { | |
return value.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment