support class literals (#4658)
This commit is contained in:
207
lib/ast.js
207
lib/ast.js
@@ -217,6 +217,12 @@ var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||
$documentation: "The empty statement (empty block or simply a semicolon)"
|
||||
}, AST_Statement);
|
||||
|
||||
function is_statement(node) {
|
||||
return node instanceof AST_Statement
|
||||
&& !(node instanceof AST_ClassExpression)
|
||||
&& !(node instanceof AST_LambdaExpression);
|
||||
}
|
||||
|
||||
function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
|
||||
multiple = multiple ? "contain" : "be";
|
||||
if (!(value instanceof AST_Node)) throw new Error(prop + " must " + multiple + " AST_Node");
|
||||
@@ -224,9 +230,7 @@ function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
|
||||
if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured");
|
||||
if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole");
|
||||
if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread");
|
||||
if (value instanceof AST_Statement && !(value instanceof AST_LambdaExpression)) {
|
||||
throw new Error(prop + " cannot " + multiple + " AST_Statement");
|
||||
}
|
||||
if (is_statement(value)) throw new Error(prop + " cannot " + multiple + " AST_Statement");
|
||||
if (value instanceof AST_SymbolDeclaration) {
|
||||
throw new Error(prop + " cannot " + multiple + " AST_SymbolDeclaration");
|
||||
}
|
||||
@@ -301,8 +305,7 @@ var AST_Block = DEFNODE("Block", "body", {
|
||||
_validate: function() {
|
||||
if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block");
|
||||
this.body.forEach(function(node) {
|
||||
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
|
||||
if (node instanceof AST_LambdaExpression) throw new Error("body cannot contain AST_LambdaExpression");
|
||||
if (!is_statement(node)) throw new Error("body must contain AST_Statement");
|
||||
});
|
||||
},
|
||||
}, AST_BlockScope);
|
||||
@@ -318,8 +321,7 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody");
|
||||
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
|
||||
if (this.body instanceof AST_LambdaExpression) throw new Error("body cannot be AST_LambdaExpression");
|
||||
if (!is_statement(this.body)) throw new Error("body must be AST_Statement");
|
||||
},
|
||||
}, AST_BlockScope);
|
||||
|
||||
@@ -416,8 +418,7 @@ var AST_For = DEFNODE("For", "init condition step", {
|
||||
_validate: function() {
|
||||
if (this.init != null) {
|
||||
if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
|
||||
if (this.init instanceof AST_Statement
|
||||
&& !(this.init instanceof AST_Definitions || this.init instanceof AST_LambdaExpression)) {
|
||||
if (is_statement(this.init) && !(this.init instanceof AST_Definitions)) {
|
||||
throw new Error("init cannot be AST_Statement");
|
||||
}
|
||||
}
|
||||
@@ -699,7 +700,7 @@ var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", {
|
||||
var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
|
||||
$documentation: "An asynchronous function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
@@ -711,7 +712,7 @@ var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
|
||||
var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
|
||||
$documentation: "An asynchronous generator function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
@@ -723,7 +724,7 @@ var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
|
||||
var AST_Function = DEFNODE("Function", "name", {
|
||||
$documentation: "A function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
@@ -735,7 +736,7 @@ var AST_Function = DEFNODE("Function", "name", {
|
||||
var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", {
|
||||
$documentation: "A generator function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
name: "[AST_SymbolLambda?] the name of this function, or null if not specified",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
@@ -772,6 +773,111 @@ var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, {
|
||||
$documentation: "A generator function definition",
|
||||
}, AST_LambdaDefinition);
|
||||
|
||||
/* -----[ classes ]----- */
|
||||
|
||||
var AST_Class = DEFNODE("Class", "extends name properties", {
|
||||
$documentation: "Base class for class literals",
|
||||
$propdoc: {
|
||||
extends: "[AST_Node?] the super class, or null if not specified",
|
||||
properties: "[AST_ClassProperty*] array of class properties",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
if (node.name) node.name.walk(visitor);
|
||||
if (node.extends) node.extends.walk(visitor);
|
||||
node.properties.forEach(function(prop) {
|
||||
prop.walk(visitor);
|
||||
});
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "Class") throw new Error("should not instantiate AST_Class");
|
||||
if (this.extends != null) must_be_expression(this, "extends");
|
||||
this.properties.forEach(function(node) {
|
||||
if (!(node instanceof AST_ClassProperty)) throw new Error("properties must contain AST_ClassProperty");
|
||||
});
|
||||
},
|
||||
}, AST_BlockScope);
|
||||
|
||||
var AST_DefClass = DEFNODE("DefClass", null, {
|
||||
$documentation: "A class definition",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolDefClass] the name of this class",
|
||||
},
|
||||
_validate: function() {
|
||||
if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass");
|
||||
},
|
||||
}, AST_Class);
|
||||
|
||||
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
|
||||
$documentation: "A class expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolClass?] the name of this class, or null if not specified",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
if (!(this.name instanceof AST_SymbolClass)) throw new Error("name must be AST_SymbolClass");
|
||||
}
|
||||
},
|
||||
}, AST_Class);
|
||||
|
||||
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
|
||||
$documentation: "Base class for `class` properties",
|
||||
$propdoc: {
|
||||
key: "[string|AST_Node] property name (AST_Node for computed property)",
|
||||
private: "[boolean] whether this is a private property",
|
||||
static: "[boolean] whether this is a static property",
|
||||
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
if (node.key instanceof AST_Node) node.key.walk(visitor);
|
||||
if (node.value) node.value.walk(visitor);
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
|
||||
if (typeof this.key != "string") {
|
||||
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
|
||||
must_be_expression(this, "key");
|
||||
}
|
||||
if(this.value != null) {
|
||||
if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var AST_ClassField = DEFNODE("ClassField", null, {
|
||||
$documentation: "A `class` field",
|
||||
_validate: function() {
|
||||
if(this.value != null) must_be_expression(this, "value");
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
var AST_ClassGetter = DEFNODE("ClassGetter", null, {
|
||||
$documentation: "A `class` getter",
|
||||
_validate: function() {
|
||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
var AST_ClassSetter = DEFNODE("ClassSetter", null, {
|
||||
$documentation: "A `class` setter",
|
||||
_validate: function() {
|
||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
var AST_ClassMethod = DEFNODE("ClassMethod", null, {
|
||||
$documentation: "A `class` method",
|
||||
_validate: function() {
|
||||
if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
|
||||
if (this.value.name != null) throw new Error("name of class method's lambda must be null");
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
/* -----[ JUMPS ]----- */
|
||||
|
||||
var AST_Jump = DEFNODE("Jump", null, {
|
||||
@@ -857,8 +963,7 @@ var AST_If = DEFNODE("If", "condition alternative", {
|
||||
_validate: function() {
|
||||
must_be_expression(this, "condition");
|
||||
if (this.alternative != null) {
|
||||
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
|
||||
if (this.alternative instanceof AST_LambdaExpression) throw new error("alternative cannot be AST_LambdaExpression");
|
||||
if (!is_statement(this.alternative)) throw new Error("alternative must be AST_Statement");
|
||||
}
|
||||
},
|
||||
}, AST_StatementWithBody);
|
||||
@@ -1042,7 +1147,7 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||
var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
|
||||
$documentation: "An `export` statement",
|
||||
$propdoc: {
|
||||
body: "[AST_Definitions|AST_LambdaDefinition] the statement to export",
|
||||
body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
@@ -1051,8 +1156,10 @@ var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (!(this.body instanceof AST_Definitions || this.body instanceof AST_LambdaDefinition)) {
|
||||
throw new Error("body must be AST_Definitions or AST_LambdaDefinition");
|
||||
if (!(this.body instanceof AST_DefClass
|
||||
|| this.body instanceof AST_Definitions
|
||||
|| this.body instanceof AST_LambdaDefinition)) {
|
||||
throw new Error("body must be AST_DefClass, AST_Definitions or AST_LambdaDefinition");
|
||||
}
|
||||
},
|
||||
}, AST_Statement);
|
||||
@@ -1069,8 +1176,14 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.body instanceof AST_Lambda && this.body.name) {
|
||||
if (!(this.body instanceof AST_LambdaDefinition)) throw new Error("body must be AST_LambdaDefinition");
|
||||
if (this.body instanceof AST_Class && this.body.name) {
|
||||
if (!(this.body instanceof AST_DefClass)) {
|
||||
throw new Error("body must be AST_DefClass when named");
|
||||
}
|
||||
} else if (this.body instanceof AST_Lambda && this.body.name) {
|
||||
if (!(this.body instanceof AST_LambdaDefinition)) {
|
||||
throw new Error("body must be AST_LambdaDefinition when named");
|
||||
}
|
||||
} else {
|
||||
must_be_expression(this, "body");
|
||||
}
|
||||
@@ -1574,6 +1687,13 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
|
||||
},
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_ObjectMethod = DEFNODE("ObjectMethod", null, {
|
||||
$documentation: "A key(){} object property",
|
||||
_validate: function() {
|
||||
if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression");
|
||||
},
|
||||
}, AST_ObjectKeyVal);
|
||||
|
||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||
$documentation: "An object setter property",
|
||||
_validate: function() {
|
||||
@@ -1609,6 +1729,16 @@ var AST_SymbolConst = DEFNODE("SymbolConst", null, {
|
||||
$documentation: "Symbol defining a constant",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
|
||||
$documentation: "Symbol defined by an `import` statement",
|
||||
$propdoc: {
|
||||
key: "[string] the original `export` name",
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.key != "string") throw new Error("key must be string");
|
||||
},
|
||||
}, AST_SymbolConst);
|
||||
|
||||
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
|
||||
$documentation: "Symbol defining a lexical-scoped variable",
|
||||
}, AST_SymbolDeclaration);
|
||||
@@ -1621,16 +1751,6 @@ var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
||||
$documentation: "Symbol naming a function argument",
|
||||
}, AST_SymbolVar);
|
||||
|
||||
var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
|
||||
$documentation: "Symbol defined by an `import` statement",
|
||||
$propdoc: {
|
||||
key: "[string] the original `export` name",
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.key != "string") throw new Error("key must be string");
|
||||
},
|
||||
}, AST_SymbolVar);
|
||||
|
||||
var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
|
||||
$documentation: "Symbol defining a function",
|
||||
}, AST_SymbolDeclaration);
|
||||
@@ -1639,6 +1759,14 @@ var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
|
||||
$documentation: "Symbol naming a function expression",
|
||||
}, AST_SymbolDeclaration);
|
||||
|
||||
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
|
||||
$documentation: "Symbol defining a class",
|
||||
}, AST_SymbolLet);
|
||||
|
||||
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
|
||||
$documentation: "Symbol naming a class expression",
|
||||
}, AST_SymbolLet);
|
||||
|
||||
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
||||
$documentation: "Symbol naming the exception in catch",
|
||||
}, AST_SymbolDeclaration);
|
||||
@@ -1672,12 +1800,26 @@ var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||
$documentation: "Reference to a label symbol",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
|
||||
$documentation: "Base class for `super` & `this`",
|
||||
_validate: function() {
|
||||
if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity");
|
||||
},
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_Super = DEFNODE("Super", null, {
|
||||
$documentation: "The `super` symbol",
|
||||
_validate: function() {
|
||||
if (this.name !== "super") throw new Error('name must be "super"');
|
||||
},
|
||||
}, AST_ObjectIdentity);
|
||||
|
||||
var AST_This = DEFNODE("This", null, {
|
||||
$documentation: "The `this` symbol",
|
||||
_validate: function() {
|
||||
if (this.name !== "this") throw new Error('name must be "this"');
|
||||
},
|
||||
}, AST_Symbol);
|
||||
}, AST_ObjectIdentity);
|
||||
|
||||
var AST_Template = DEFNODE("Template", "expressions strings tag", {
|
||||
$documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
|
||||
@@ -1837,7 +1979,8 @@ TreeWalker.prototype = {
|
||||
this.stack.push(node);
|
||||
},
|
||||
pop: function() {
|
||||
if (this.stack.pop() instanceof AST_Lambda) {
|
||||
var node = this.stack.pop();
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.getPrototypeOf(this.directives);
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user