lexer/parser: split out `null` and `undefined` from `BOOL` token
also made explicit AST nodes `Bool`, `Null`, and `Undefined`
This commit is contained in:
parent
46065199cd
commit
caf3da2f66
|
@ -64,13 +64,12 @@
|
||||||
return new Literal($1);
|
return new Literal($1);
|
||||||
}), o('DEBUGGER', function() {
|
}), o('DEBUGGER', function() {
|
||||||
return new Literal($1);
|
return new Literal($1);
|
||||||
|
}), o('UNDEFINED', function() {
|
||||||
|
return new Undefined;
|
||||||
|
}), o('NULL', function() {
|
||||||
|
return new Null;
|
||||||
}), o('BOOL', function() {
|
}), o('BOOL', function() {
|
||||||
var val;
|
return new Bool($1);
|
||||||
val = new Literal($1);
|
|
||||||
if ($1 === 'undefined') {
|
|
||||||
val.isUndefined = true;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
Assign: [
|
Assign: [
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Lexer.prototype.identifierToken = function() {
|
Lexer.prototype.identifierToken = function() {
|
||||||
var colon, forcedIdentifier, id, input, match, prev, tag, _ref2, _ref3;
|
var colon, forcedIdentifier, id, input, match, originalId, prev, tag, _ref2, _ref3;
|
||||||
if (!(match = IDENTIFIER.exec(this.chunk))) {
|
if (!(match = IDENTIFIER.exec(this.chunk))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!forcedIdentifier) {
|
if (!forcedIdentifier) {
|
||||||
|
originalId = id;
|
||||||
if (__indexOf.call(COFFEE_ALIASES, id) >= 0) {
|
if (__indexOf.call(COFFEE_ALIASES, id) >= 0) {
|
||||||
id = COFFEE_ALIAS_MAP[id];
|
id = COFFEE_ALIAS_MAP[id];
|
||||||
}
|
}
|
||||||
|
@ -102,9 +103,12 @@
|
||||||
return 'LOGIC';
|
return 'LOGIC';
|
||||||
case 'true':
|
case 'true':
|
||||||
case 'false':
|
case 'false':
|
||||||
case 'null':
|
id = originalId;
|
||||||
case 'undefined':
|
|
||||||
return 'BOOL';
|
return 'BOOL';
|
||||||
|
case 'undefined':
|
||||||
|
return 'UNDEFINED';
|
||||||
|
case 'null':
|
||||||
|
return 'NULL';
|
||||||
case 'break':
|
case 'break':
|
||||||
case 'continue':
|
case 'continue':
|
||||||
return 'STATEMENT';
|
return 'STATEMENT';
|
||||||
|
@ -775,15 +779,15 @@
|
||||||
|
|
||||||
RELATION = ['IN', 'OF', 'INSTANCEOF'];
|
RELATION = ['IN', 'OF', 'INSTANCEOF'];
|
||||||
|
|
||||||
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED'];
|
BOOL = ['TRUE', 'FALSE'];
|
||||||
|
|
||||||
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']'];
|
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']'];
|
||||||
|
|
||||||
NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING');
|
NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING');
|
||||||
|
|
||||||
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
|
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
|
||||||
|
|
||||||
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL');
|
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL', 'NULL', 'UNDEFINED');
|
||||||
|
|
||||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||||
|
|
||||||
|
|
|
@ -466,7 +466,7 @@
|
||||||
|
|
||||||
Literal.prototype.compileNode = function(o) {
|
Literal.prototype.compileNode = function(o) {
|
||||||
var code, _ref2;
|
var code, _ref2;
|
||||||
code = this.isUndefined ? o.level >= LEVEL_ACCESS ? '(void 0)' : 'void 0' : this.value === 'this' ? ((_ref2 = o.scope.method) != null ? _ref2.bound : void 0) ? o.scope.method.context : this.value : this.value.reserved ? "\"" + this.value + "\"" : this.value;
|
code = this.value === 'this' ? ((_ref2 = o.scope.method) != null ? _ref2.bound : void 0) ? o.scope.method.context : this.value : this.value.reserved ? "\"" + this.value + "\"" : this.value;
|
||||||
if (this.isStatement()) {
|
if (this.isStatement()) {
|
||||||
return "" + this.tab + code + ";";
|
return "" + this.tab + code + ";";
|
||||||
} else {
|
} else {
|
||||||
|
@ -482,6 +482,79 @@
|
||||||
|
|
||||||
})(Base);
|
})(Base);
|
||||||
|
|
||||||
|
exports.Undefined = (function(_super) {
|
||||||
|
|
||||||
|
__extends(Undefined, _super);
|
||||||
|
|
||||||
|
function Undefined() {
|
||||||
|
return Undefined.__super__.constructor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Undefined.prototype.isAssignable = NO;
|
||||||
|
|
||||||
|
Undefined.prototype.isComplex = NO;
|
||||||
|
|
||||||
|
Undefined.prototype.compileNode = function(o) {
|
||||||
|
if (o.level >= LEVEL_ACCESS) {
|
||||||
|
return '(void 0)';
|
||||||
|
} else {
|
||||||
|
return 'void 0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Undefined;
|
||||||
|
|
||||||
|
})(Base);
|
||||||
|
|
||||||
|
exports.Null = (function(_super) {
|
||||||
|
|
||||||
|
__extends(Null, _super);
|
||||||
|
|
||||||
|
function Null() {
|
||||||
|
return Null.__super__.constructor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Null.prototype.isAssignable = NO;
|
||||||
|
|
||||||
|
Null.prototype.isComplex = NO;
|
||||||
|
|
||||||
|
Null.prototype.compileNode = function() {
|
||||||
|
return "null";
|
||||||
|
};
|
||||||
|
|
||||||
|
return Null;
|
||||||
|
|
||||||
|
})(Base);
|
||||||
|
|
||||||
|
exports.Bool = (function(_super) {
|
||||||
|
|
||||||
|
__extends(Bool, _super);
|
||||||
|
|
||||||
|
Bool.prototype.isAssignable = NO;
|
||||||
|
|
||||||
|
Bool.prototype.isComplex = NO;
|
||||||
|
|
||||||
|
Bool.prototype.compileNode = function() {
|
||||||
|
switch (this.val) {
|
||||||
|
case 'yes':
|
||||||
|
case 'on':
|
||||||
|
return 'true';
|
||||||
|
case 'no':
|
||||||
|
case 'off':
|
||||||
|
return 'false';
|
||||||
|
default:
|
||||||
|
return this.val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Bool(val) {
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bool;
|
||||||
|
|
||||||
|
})(Base);
|
||||||
|
|
||||||
exports.Return = Return = (function(_super) {
|
exports.Return = Return = (function(_super) {
|
||||||
|
|
||||||
__extends(Return, _super);
|
__extends(Return, _super);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -332,7 +332,7 @@
|
||||||
|
|
||||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
||||||
|
|
||||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||||
|
|
||||||
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,9 @@ grammar =
|
||||||
o 'JS', -> new Literal $1
|
o 'JS', -> new Literal $1
|
||||||
o 'REGEX', -> new Literal $1
|
o 'REGEX', -> new Literal $1
|
||||||
o 'DEBUGGER', -> new Literal $1
|
o 'DEBUGGER', -> new Literal $1
|
||||||
o 'BOOL', ->
|
o 'UNDEFINED', -> new Undefined
|
||||||
val = new Literal $1
|
o 'NULL', -> new Null
|
||||||
val.isUndefined = yes if $1 is 'undefined'
|
o 'BOOL', -> new Bool $1
|
||||||
val
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Assignment of a variable, property, or index to a value.
|
# Assignment of a variable, property, or index to a value.
|
||||||
|
|
|
@ -115,12 +115,17 @@ exports.Lexer = class Lexer
|
||||||
@error "reserved word \"#{id}\""
|
@error "reserved word \"#{id}\""
|
||||||
|
|
||||||
unless forcedIdentifier
|
unless forcedIdentifier
|
||||||
|
originalId = id
|
||||||
id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
|
id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
|
||||||
tag = switch id
|
tag = switch id
|
||||||
when '!' then 'UNARY'
|
when '!' then 'UNARY'
|
||||||
when '==', '!=' then 'COMPARE'
|
when '==', '!=' then 'COMPARE'
|
||||||
when '&&', '||' then 'LOGIC'
|
when '&&', '||' then 'LOGIC'
|
||||||
when 'true', 'false', 'null', 'undefined' then 'BOOL'
|
when 'true', 'false'
|
||||||
|
id = originalId
|
||||||
|
'BOOL'
|
||||||
|
when 'undefined' then 'UNDEFINED'
|
||||||
|
when 'null' then 'NULL'
|
||||||
when 'break', 'continue' then 'STATEMENT'
|
when 'break', 'continue' then 'STATEMENT'
|
||||||
else tag
|
else tag
|
||||||
|
|
||||||
|
@ -684,7 +689,7 @@ MATH = ['*', '/', '%']
|
||||||
RELATION = ['IN', 'OF', 'INSTANCEOF']
|
RELATION = ['IN', 'OF', 'INSTANCEOF']
|
||||||
|
|
||||||
# Boolean tokens.
|
# Boolean tokens.
|
||||||
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']
|
BOOL = ['TRUE', 'FALSE']
|
||||||
|
|
||||||
# Tokens which a regular expression will never immediately follow, but which
|
# Tokens which a regular expression will never immediately follow, but which
|
||||||
# a division operator might.
|
# a division operator might.
|
||||||
|
@ -692,7 +697,7 @@ BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']
|
||||||
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
|
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
|
||||||
#
|
#
|
||||||
# Our list is shorter, due to sans-parentheses method calls.
|
# Our list is shorter, due to sans-parentheses method calls.
|
||||||
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']']
|
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']']
|
||||||
|
|
||||||
# If the previous token is not spaced, there are more preceding tokens that
|
# If the previous token is not spaced, there are more preceding tokens that
|
||||||
# force a division parse:
|
# force a division parse:
|
||||||
|
@ -702,7 +707,7 @@ NOT_SPACED_REGEX = NOT_REGEX.concat ')', '}', 'THIS', 'IDENTIFIER', 'STRING'
|
||||||
# parentheses or bracket following these tokens will be recorded as the start
|
# parentheses or bracket following these tokens will be recorded as the start
|
||||||
# of a function invocation or indexing operation.
|
# of a function invocation or indexing operation.
|
||||||
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']
|
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']
|
||||||
INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL'
|
INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED'
|
||||||
|
|
||||||
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
|
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
|
||||||
# occurs at the start of a line. We disambiguate these from trailing whens to
|
# occurs at the start of a line. We disambiguate these from trailing whens to
|
||||||
|
|
|
@ -325,9 +325,7 @@ exports.Literal = class Literal extends Base
|
||||||
return this if @value is 'continue' and not o?.loop
|
return this if @value is 'continue' and not o?.loop
|
||||||
|
|
||||||
compileNode: (o) ->
|
compileNode: (o) ->
|
||||||
code = if @isUndefined
|
code = if @value is 'this'
|
||||||
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
|
|
||||||
else if @value is 'this'
|
|
||||||
if o.scope.method?.bound then o.scope.method.context else @value
|
if o.scope.method?.bound then o.scope.method.context else @value
|
||||||
else if @value.reserved
|
else if @value.reserved
|
||||||
"\"#{@value}\""
|
"\"#{@value}\""
|
||||||
|
@ -338,6 +336,27 @@ exports.Literal = class Literal extends Base
|
||||||
toString: ->
|
toString: ->
|
||||||
' "' + @value + '"'
|
' "' + @value + '"'
|
||||||
|
|
||||||
|
class exports.Undefined extends Base
|
||||||
|
isAssignable: NO
|
||||||
|
isComplex: NO
|
||||||
|
compileNode: (o) ->
|
||||||
|
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
|
||||||
|
|
||||||
|
class exports.Null extends Base
|
||||||
|
isAssignable: NO
|
||||||
|
isComplex: NO
|
||||||
|
compileNode: -> "null"
|
||||||
|
|
||||||
|
class exports.Bool extends Base
|
||||||
|
isAssignable: NO
|
||||||
|
isComplex: NO
|
||||||
|
compileNode: ->
|
||||||
|
switch @val
|
||||||
|
when 'yes', 'on' then 'true'
|
||||||
|
when 'no', 'off' then 'false'
|
||||||
|
else @val
|
||||||
|
constructor: (@val) ->
|
||||||
|
|
||||||
#### Return
|
#### Return
|
||||||
|
|
||||||
# A `return` is a *pureStatement* -- wrapping it in a closure wouldn't
|
# A `return` is a *pureStatement* -- wrapping it in a closure wouldn't
|
||||||
|
|
|
@ -297,7 +297,7 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
|
||||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||||
IMPLICIT_CALL = [
|
IMPLICIT_CALL = [
|
||||||
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
|
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
|
||||||
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER'
|
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER'
|
||||||
'@', '->', '=>', '[', '(', '{', '--', '++'
|
'@', '->', '=>', '[', '(', '{', '--', '++'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue