lexer/parser: split out `null` and `undefined` from `BOOL` token

also made explicit AST nodes `Bool`, `Null`, and `Undefined`
This commit is contained in:
Michael Ficarra 2012-05-08 16:11:23 -04:00
parent 46065199cd
commit caf3da2f66
9 changed files with 312 additions and 214 deletions

View File

@ -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: [

View File

@ -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'];

View File

@ -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

View File

@ -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 = ['+', '-'];

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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'
'@', '->', '=>', '[', '(', '{', '--', '++'
]