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); 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: [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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