first draft at ticket #437 ... automatic quoting of reserved words and keywords.
This commit is contained in:
parent
d0948e5586
commit
4b284f6687
48
lib/lexer.js
48
lib/lexer.js
|
@ -128,22 +128,32 @@
|
|||
// referenced as property names here, so you can still do `jQuery.is()` even
|
||||
// though `is` means `===` otherwise.
|
||||
Lexer.prototype.identifierToken = function() {
|
||||
var forcedIdentifier, id, tag;
|
||||
var close_index, forcedIdentifier, id, tag;
|
||||
if (!(id = this.match(IDENTIFIER, 1))) {
|
||||
return false;
|
||||
}
|
||||
this.i += id.length;
|
||||
forcedIdentifier = this.tagAccessor() || this.match(ASSIGNED, 1);
|
||||
tag = 'IDENTIFIER';
|
||||
if (include(JS_KEYWORDS, id) || (!forcedIdentifier && include(COFFEE_KEYWORDS, id))) {
|
||||
tag = id.toUpperCase();
|
||||
}
|
||||
if (include(RESERVED, id)) {
|
||||
this.identifierError(id);
|
||||
}
|
||||
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
|
||||
tag = 'LEADING_WHEN';
|
||||
}
|
||||
this.i += id.length;
|
||||
if (include(JS_FORBIDDEN, id)) {
|
||||
if (forcedIdentifier) {
|
||||
tag = 'STRING';
|
||||
id = ("'" + id + "'");
|
||||
if (forcedIdentifier === 'accessor') {
|
||||
close_index = true;
|
||||
this.tokens.pop();
|
||||
this.token('INDEX_START', '[');
|
||||
}
|
||||
} else if (include(RESERVED, id)) {
|
||||
this.identifierError(id);
|
||||
}
|
||||
}
|
||||
if (!(forcedIdentifier)) {
|
||||
if (include(COFFEE_ALIASES, id)) {
|
||||
tag = (id = CONVERSIONS[id]);
|
||||
|
@ -153,6 +163,9 @@
|
|||
}
|
||||
}
|
||||
this.token(tag, id);
|
||||
if (close_index) {
|
||||
this.token(']', ']');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Matches numbers, including decimals, hex, and exponential notation.
|
||||
|
@ -419,21 +432,28 @@
|
|||
// if it's a special kind of accessor. Return `true` if any type of accessor
|
||||
// is the previous token.
|
||||
Lexer.prototype.tagAccessor = function() {
|
||||
var prev;
|
||||
var accessor, prev;
|
||||
if ((!(prev = this.prev())) || (prev && prev.spaced)) {
|
||||
return false;
|
||||
}
|
||||
if (prev[1] === '::') {
|
||||
return this.tag(1, 'PROTOTYPE_ACCESS');
|
||||
} else if (prev[1] === '.' && !(this.value(2) === '.')) {
|
||||
if (this.tag(2) === '?') {
|
||||
this.tag(1, 'SOAK_ACCESS');
|
||||
return this.tokens.splice(-2, 1);
|
||||
accessor = (function() {
|
||||
if (prev[1] === '::') {
|
||||
return this.tag(1, 'PROTOTYPE_ACCESS');
|
||||
} else if (prev[1] === '.' && !(this.value(2) === '.')) {
|
||||
if (this.tag(2) === '?') {
|
||||
this.tag(1, 'SOAK_ACCESS');
|
||||
return this.tokens.splice(-2, 1);
|
||||
} else {
|
||||
return this.tag(1, 'PROPERTY_ACCESS');
|
||||
}
|
||||
} else {
|
||||
return this.tag(1, 'PROPERTY_ACCESS');
|
||||
return prev[0] === '@';
|
||||
}
|
||||
}).call(this);
|
||||
if (accessor) {
|
||||
return 'accessor';
|
||||
} else {
|
||||
return prev[0] === '@';
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||
|
|
|
@ -651,7 +651,7 @@
|
|||
exports.AccessorNode = (function() {
|
||||
AccessorNode = function(name, tag) {
|
||||
this.name = name;
|
||||
this.prototype = tag === 'prototype';
|
||||
this.prototype = tag === 'prototype' ? '.prototype' : '';
|
||||
this.soakNode = tag === 'soak';
|
||||
return this;
|
||||
};
|
||||
|
@ -659,10 +659,11 @@
|
|||
AccessorNode.prototype.type = 'AccessorNode';
|
||||
AccessorNode.prototype.children = ['name'];
|
||||
AccessorNode.prototype.compileNode = function(o) {
|
||||
var protoPart;
|
||||
var name, namePart;
|
||||
name = this.name.compile(o);
|
||||
o.chainRoot.wrapped = o.chainRoot.wrapped || this.soakNode;
|
||||
protoPart = this.prototype ? 'prototype.' : '';
|
||||
return "." + protoPart + (this.name.compile(o));
|
||||
namePart = name.match(IS_STRING) ? ("[" + name + "]") : ("." + name);
|
||||
return this.prototype + namePart;
|
||||
};
|
||||
return AccessorNode;
|
||||
})();
|
||||
|
|
|
@ -90,16 +90,26 @@ exports.Lexer: class Lexer
|
|||
# though `is` means `===` otherwise.
|
||||
identifierToken: ->
|
||||
return false unless id: @match IDENTIFIER, 1
|
||||
@i: + id.length
|
||||
forcedIdentifier: @tagAccessor() or @match ASSIGNED, 1
|
||||
tag: 'IDENTIFIER'
|
||||
tag: id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
|
||||
@identifierError id if include RESERVED, id
|
||||
tag: 'LEADING_WHEN' if tag is 'WHEN' and include LINE_BREAK, @tag()
|
||||
@i: + id.length
|
||||
if include(JS_FORBIDDEN, id)
|
||||
if forcedIdentifier
|
||||
tag: 'STRING'
|
||||
id: "'$id'"
|
||||
if forcedIdentifier is 'accessor'
|
||||
close_index: true
|
||||
@tokens.pop()
|
||||
@token 'INDEX_START', '['
|
||||
else if include(RESERVED, id)
|
||||
@identifierError id
|
||||
unless forcedIdentifier
|
||||
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
|
||||
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
|
||||
@token tag, id
|
||||
@token ']', ']' if close_index
|
||||
true
|
||||
|
||||
# Matches numbers, including decimals, hex, and exponential notation.
|
||||
|
@ -289,7 +299,7 @@ exports.Lexer: class Lexer
|
|||
# is the previous token.
|
||||
tagAccessor: ->
|
||||
return false if (not prev: @prev()) or (prev and prev.spaced)
|
||||
if prev[1] is '::'
|
||||
accessor: if prev[1] is '::'
|
||||
@tag 1, 'PROTOTYPE_ACCESS'
|
||||
else if prev[1] is '.' and not (@value(2) is '.')
|
||||
if @tag(2) is '?'
|
||||
|
@ -299,6 +309,7 @@ exports.Lexer: class Lexer
|
|||
@tag 1, 'PROPERTY_ACCESS'
|
||||
else
|
||||
prev[0] is '@'
|
||||
if accessor then 'accessor' else false
|
||||
|
||||
# Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||
# erasing all external indentation on the left-hand side.
|
||||
|
|
|
@ -474,13 +474,14 @@ exports.AccessorNode: class AccessorNode extends BaseNode
|
|||
|
||||
constructor: (name, tag) ->
|
||||
@name: name
|
||||
@prototype: tag is 'prototype'
|
||||
@prototype: if tag is 'prototype' then '.prototype' else ''
|
||||
@soakNode: tag is 'soak'
|
||||
|
||||
compileNode: (o) ->
|
||||
name: @name.compile o
|
||||
o.chainRoot.wrapped: or @soakNode
|
||||
protoPart: if @prototype then 'prototype.' else ''
|
||||
".$protoPart${@name.compile(o)}"
|
||||
namePart: if name.match(IS_STRING) then "[$name]" else ".$name"
|
||||
@prototype + namePart
|
||||
|
||||
#### IndexNode
|
||||
|
||||
|
|
|
@ -117,4 +117,11 @@ class Hive.Bee extends Hive
|
|||
constructor: (name) -> super name
|
||||
|
||||
maya: new Hive.Bee 'Maya'
|
||||
ok maya.name is 'Maya'
|
||||
ok maya.name is 'Maya'
|
||||
|
||||
|
||||
# Class with JS-keyword properties.
|
||||
class Class
|
||||
class: 'class'
|
||||
|
||||
ok (new Class()).class is 'class'
|
|
@ -107,3 +107,10 @@ result: [['a']
|
|||
|
||||
ok result[0][0] is 'a'
|
||||
ok result[1]['b'] is 'c'
|
||||
|
||||
|
||||
# Object literals should be able to include keywords.
|
||||
obj: {class: 'hot'}
|
||||
obj.function: 'dog'
|
||||
|
||||
ok obj.class + obj.function is 'hotdog'
|
||||
|
|
Loading…
Reference in New Issue