mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
first draft at ticket #437 ... automatic quoting of reserved words and keywords.
This commit is contained in:
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
|
// referenced as property names here, so you can still do `jQuery.is()` even
|
||||||
// though `is` means `===` otherwise.
|
// though `is` means `===` otherwise.
|
||||||
Lexer.prototype.identifierToken = function() {
|
Lexer.prototype.identifierToken = function() {
|
||||||
var forcedIdentifier, id, tag;
|
var close_index, forcedIdentifier, id, tag;
|
||||||
if (!(id = this.match(IDENTIFIER, 1))) {
|
if (!(id = this.match(IDENTIFIER, 1))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
this.i += id.length;
|
||||||
forcedIdentifier = this.tagAccessor() || this.match(ASSIGNED, 1);
|
forcedIdentifier = this.tagAccessor() || this.match(ASSIGNED, 1);
|
||||||
tag = 'IDENTIFIER';
|
tag = 'IDENTIFIER';
|
||||||
if (include(JS_KEYWORDS, id) || (!forcedIdentifier && include(COFFEE_KEYWORDS, id))) {
|
if (include(JS_KEYWORDS, id) || (!forcedIdentifier && include(COFFEE_KEYWORDS, id))) {
|
||||||
tag = id.toUpperCase();
|
tag = id.toUpperCase();
|
||||||
}
|
}
|
||||||
if (include(RESERVED, id)) {
|
|
||||||
this.identifierError(id);
|
|
||||||
}
|
|
||||||
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
|
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
|
||||||
tag = 'LEADING_WHEN';
|
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 (!(forcedIdentifier)) {
|
||||||
if (include(COFFEE_ALIASES, id)) {
|
if (include(COFFEE_ALIASES, id)) {
|
||||||
tag = (id = CONVERSIONS[id]);
|
tag = (id = CONVERSIONS[id]);
|
||||||
@@ -153,6 +163,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.token(tag, id);
|
this.token(tag, id);
|
||||||
|
if (close_index) {
|
||||||
|
this.token(']', ']');
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Matches numbers, including decimals, hex, and exponential notation.
|
// 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
|
// if it's a special kind of accessor. Return `true` if any type of accessor
|
||||||
// is the previous token.
|
// is the previous token.
|
||||||
Lexer.prototype.tagAccessor = function() {
|
Lexer.prototype.tagAccessor = function() {
|
||||||
var prev;
|
var accessor, prev;
|
||||||
if ((!(prev = this.prev())) || (prev && prev.spaced)) {
|
if ((!(prev = this.prev())) || (prev && prev.spaced)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (prev[1] === '::') {
|
accessor = (function() {
|
||||||
return this.tag(1, 'PROTOTYPE_ACCESS');
|
if (prev[1] === '::') {
|
||||||
} else if (prev[1] === '.' && !(this.value(2) === '.')) {
|
return this.tag(1, 'PROTOTYPE_ACCESS');
|
||||||
if (this.tag(2) === '?') {
|
} else if (prev[1] === '.' && !(this.value(2) === '.')) {
|
||||||
this.tag(1, 'SOAK_ACCESS');
|
if (this.tag(2) === '?') {
|
||||||
return this.tokens.splice(-2, 1);
|
this.tag(1, 'SOAK_ACCESS');
|
||||||
|
return this.tokens.splice(-2, 1);
|
||||||
|
} else {
|
||||||
|
return this.tag(1, 'PROPERTY_ACCESS');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return this.tag(1, 'PROPERTY_ACCESS');
|
return prev[0] === '@';
|
||||||
}
|
}
|
||||||
|
}).call(this);
|
||||||
|
if (accessor) {
|
||||||
|
return 'accessor';
|
||||||
} else {
|
} else {
|
||||||
return prev[0] === '@';
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Sanitize a heredoc or herecomment by escaping internal double quotes and
|
// Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||||
|
|||||||
@@ -651,7 +651,7 @@
|
|||||||
exports.AccessorNode = (function() {
|
exports.AccessorNode = (function() {
|
||||||
AccessorNode = function(name, tag) {
|
AccessorNode = function(name, tag) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.prototype = tag === 'prototype';
|
this.prototype = tag === 'prototype' ? '.prototype' : '';
|
||||||
this.soakNode = tag === 'soak';
|
this.soakNode = tag === 'soak';
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@@ -659,10 +659,11 @@
|
|||||||
AccessorNode.prototype.type = 'AccessorNode';
|
AccessorNode.prototype.type = 'AccessorNode';
|
||||||
AccessorNode.prototype.children = ['name'];
|
AccessorNode.prototype.children = ['name'];
|
||||||
AccessorNode.prototype.compileNode = function(o) {
|
AccessorNode.prototype.compileNode = function(o) {
|
||||||
var protoPart;
|
var name, namePart;
|
||||||
|
name = this.name.compile(o);
|
||||||
o.chainRoot.wrapped = o.chainRoot.wrapped || this.soakNode;
|
o.chainRoot.wrapped = o.chainRoot.wrapped || this.soakNode;
|
||||||
protoPart = this.prototype ? 'prototype.' : '';
|
namePart = name.match(IS_STRING) ? ("[" + name + "]") : ("." + name);
|
||||||
return "." + protoPart + (this.name.compile(o));
|
return this.prototype + namePart;
|
||||||
};
|
};
|
||||||
return AccessorNode;
|
return AccessorNode;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -90,16 +90,26 @@ exports.Lexer: class Lexer
|
|||||||
# though `is` means `===` otherwise.
|
# though `is` means `===` otherwise.
|
||||||
identifierToken: ->
|
identifierToken: ->
|
||||||
return false unless id: @match IDENTIFIER, 1
|
return false unless id: @match IDENTIFIER, 1
|
||||||
|
@i: + id.length
|
||||||
forcedIdentifier: @tagAccessor() or @match ASSIGNED, 1
|
forcedIdentifier: @tagAccessor() or @match ASSIGNED, 1
|
||||||
tag: 'IDENTIFIER'
|
tag: 'IDENTIFIER'
|
||||||
tag: id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
|
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()
|
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
|
unless forcedIdentifier
|
||||||
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
|
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
|
||||||
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
|
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
|
||||||
@token tag, id
|
@token tag, id
|
||||||
|
@token ']', ']' if close_index
|
||||||
true
|
true
|
||||||
|
|
||||||
# Matches numbers, including decimals, hex, and exponential notation.
|
# Matches numbers, including decimals, hex, and exponential notation.
|
||||||
@@ -289,7 +299,7 @@ exports.Lexer: class Lexer
|
|||||||
# is the previous token.
|
# is the previous token.
|
||||||
tagAccessor: ->
|
tagAccessor: ->
|
||||||
return false if (not prev: @prev()) or (prev and prev.spaced)
|
return false if (not prev: @prev()) or (prev and prev.spaced)
|
||||||
if prev[1] is '::'
|
accessor: if prev[1] is '::'
|
||||||
@tag 1, 'PROTOTYPE_ACCESS'
|
@tag 1, 'PROTOTYPE_ACCESS'
|
||||||
else if prev[1] is '.' and not (@value(2) is '.')
|
else if prev[1] is '.' and not (@value(2) is '.')
|
||||||
if @tag(2) is '?'
|
if @tag(2) is '?'
|
||||||
@@ -299,6 +309,7 @@ exports.Lexer: class Lexer
|
|||||||
@tag 1, 'PROPERTY_ACCESS'
|
@tag 1, 'PROPERTY_ACCESS'
|
||||||
else
|
else
|
||||||
prev[0] is '@'
|
prev[0] is '@'
|
||||||
|
if accessor then 'accessor' else false
|
||||||
|
|
||||||
# Sanitize a heredoc or herecomment by escaping internal double quotes and
|
# Sanitize a heredoc or herecomment by escaping internal double quotes and
|
||||||
# erasing all external indentation on the left-hand side.
|
# erasing all external indentation on the left-hand side.
|
||||||
|
|||||||
@@ -474,13 +474,14 @@ exports.AccessorNode: class AccessorNode extends BaseNode
|
|||||||
|
|
||||||
constructor: (name, tag) ->
|
constructor: (name, tag) ->
|
||||||
@name: name
|
@name: name
|
||||||
@prototype: tag is 'prototype'
|
@prototype: if tag is 'prototype' then '.prototype' else ''
|
||||||
@soakNode: tag is 'soak'
|
@soakNode: tag is 'soak'
|
||||||
|
|
||||||
compileNode: (o) ->
|
compileNode: (o) ->
|
||||||
|
name: @name.compile o
|
||||||
o.chainRoot.wrapped: or @soakNode
|
o.chainRoot.wrapped: or @soakNode
|
||||||
protoPart: if @prototype then 'prototype.' else ''
|
namePart: if name.match(IS_STRING) then "[$name]" else ".$name"
|
||||||
".$protoPart${@name.compile(o)}"
|
@prototype + namePart
|
||||||
|
|
||||||
#### IndexNode
|
#### IndexNode
|
||||||
|
|
||||||
|
|||||||
@@ -117,4 +117,11 @@ class Hive.Bee extends Hive
|
|||||||
constructor: (name) -> super name
|
constructor: (name) -> super name
|
||||||
|
|
||||||
maya: new Hive.Bee 'Maya'
|
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[0][0] is 'a'
|
||||||
ok result[1]['b'] is 'c'
|
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'
|
||||||
|
|||||||
Reference in New Issue
Block a user