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);
|
||||
}), o('DEBUGGER', function() {
|
||||
return new Literal($1);
|
||||
}), o('UNDEFINED', function() {
|
||||
return new Undefined;
|
||||
}), o('NULL', function() {
|
||||
return new Null;
|
||||
}), o('BOOL', function() {
|
||||
var val;
|
||||
val = new Literal($1);
|
||||
if ($1 === 'undefined') {
|
||||
val.isUndefined = true;
|
||||
}
|
||||
return val;
|
||||
return new Bool($1);
|
||||
})
|
||||
],
|
||||
Assign: [
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
};
|
||||
|
||||
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))) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -87,6 +87,7 @@
|
|||
}
|
||||
}
|
||||
if (!forcedIdentifier) {
|
||||
originalId = id;
|
||||
if (__indexOf.call(COFFEE_ALIASES, id) >= 0) {
|
||||
id = COFFEE_ALIAS_MAP[id];
|
||||
}
|
||||
|
@ -102,9 +103,12 @@
|
|||
return 'LOGIC';
|
||||
case 'true':
|
||||
case 'false':
|
||||
case 'null':
|
||||
case 'undefined':
|
||||
id = originalId;
|
||||
return 'BOOL';
|
||||
case 'undefined':
|
||||
return 'UNDEFINED';
|
||||
case 'null':
|
||||
return 'NULL';
|
||||
case 'break':
|
||||
case 'continue':
|
||||
return 'STATEMENT';
|
||||
|
@ -775,15 +779,15 @@
|
|||
|
||||
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');
|
||||
|
||||
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
|
||||
|
||||
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL');
|
||||
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL', 'NULL', 'UNDEFINED');
|
||||
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||
|
||||
|
|
|
@ -466,7 +466,7 @@
|
|||
|
||||
Literal.prototype.compileNode = function(o) {
|
||||
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()) {
|
||||
return "" + this.tab + code + ";";
|
||||
} else {
|
||||
|
@ -482,6 +482,79 @@
|
|||
|
||||
})(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) {
|
||||
|
||||
__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_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 = ['+', '-'];
|
||||
|
||||
|
|
|
@ -127,10 +127,9 @@ grammar =
|
|||
o 'JS', -> new Literal $1
|
||||
o 'REGEX', -> new Literal $1
|
||||
o 'DEBUGGER', -> new Literal $1
|
||||
o 'BOOL', ->
|
||||
val = new Literal $1
|
||||
val.isUndefined = yes if $1 is 'undefined'
|
||||
val
|
||||
o 'UNDEFINED', -> new Undefined
|
||||
o 'NULL', -> new Null
|
||||
o 'BOOL', -> new Bool $1
|
||||
]
|
||||
|
||||
# Assignment of a variable, property, or index to a value.
|
||||
|
|
|
@ -115,13 +115,18 @@ exports.Lexer = class Lexer
|
|||
@error "reserved word \"#{id}\""
|
||||
|
||||
unless forcedIdentifier
|
||||
originalId = id
|
||||
id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
|
||||
tag = switch id
|
||||
when '!' then 'UNARY'
|
||||
when '==', '!=' then 'COMPARE'
|
||||
when '&&', '||' then 'LOGIC'
|
||||
when 'true', 'false', 'null', 'undefined' then 'BOOL'
|
||||
when 'break', 'continue' then 'STATEMENT'
|
||||
when '!' then 'UNARY'
|
||||
when '==', '!=' then 'COMPARE'
|
||||
when '&&', '||' then 'LOGIC'
|
||||
when 'true', 'false'
|
||||
id = originalId
|
||||
'BOOL'
|
||||
when 'undefined' then 'UNDEFINED'
|
||||
when 'null' then 'NULL'
|
||||
when 'break', 'continue' then 'STATEMENT'
|
||||
else tag
|
||||
|
||||
@token tag, id
|
||||
|
@ -684,7 +689,7 @@ MATH = ['*', '/', '%']
|
|||
RELATION = ['IN', 'OF', 'INSTANCEOF']
|
||||
|
||||
# Boolean tokens.
|
||||
BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']
|
||||
BOOL = ['TRUE', 'FALSE']
|
||||
|
||||
# Tokens which a regular expression will never immediately follow, but which
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# of a function invocation or indexing operation.
|
||||
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`
|
||||
# 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
|
||||
|
||||
compileNode: (o) ->
|
||||
code = if @isUndefined
|
||||
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
|
||||
else if @value is 'this'
|
||||
code = if @value is 'this'
|
||||
if o.scope.method?.bound then o.scope.method.context else @value
|
||||
else if @value.reserved
|
||||
"\"#{@value}\""
|
||||
|
@ -338,6 +336,27 @@ exports.Literal = class Literal extends Base
|
|||
toString: ->
|
||||
' "' + @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
|
||||
|
||||
# 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.
|
||||
IMPLICIT_CALL = [
|
||||
'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