mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
lexer now distinguishes between IN/OF and FORIN/FOROF to help grammar, fixing #737
This commit is contained in:
@@ -476,13 +476,13 @@ grammar =
|
||||
# clause. If it's an array comprehension, you can also choose to step through
|
||||
# in fixed-size increments.
|
||||
ForSource: [
|
||||
o "IN Expression", -> source: $2
|
||||
o "OF Expression", -> source: $2, object: true
|
||||
o "IN Expression WHEN Expression", -> source: $2, guard: $4
|
||||
o "OF Expression WHEN Expression", -> source: $2, guard: $4, object: true
|
||||
o "IN Expression BY Expression", -> source: $2, step: $4
|
||||
o "IN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6
|
||||
o "IN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
|
||||
o "FORIN Expression", -> source: $2
|
||||
o "FOROF Expression", -> source: $2, object: true
|
||||
o "FORIN Expression WHEN Expression", -> source: $2, guard: $4
|
||||
o "FOROF Expression WHEN Expression", -> source: $2, guard: $4, object: true
|
||||
o "FORIN Expression BY Expression", -> source: $2, step: $4
|
||||
o "FORIN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6
|
||||
o "FORIN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
|
||||
]
|
||||
|
||||
Switch: [
|
||||
@@ -552,14 +552,14 @@ grammar =
|
||||
o "Value COMPOUND_ASSIGN Expression", -> new OpNode $2, $1, $3
|
||||
o "Value COMPOUND_ASSIGN INDENT Expression OUTDENT", -> new OpNode $2, $1, $4
|
||||
|
||||
o "Expression IN Expression", -> new InNode $1, $3
|
||||
o "Expression OF Expression", -> new OpNode $2, $1, $3
|
||||
o "Expression INSTANCEOF Expression", -> new OpNode $2, $1, $3
|
||||
o "Expression NOT_RELATED Expression", ->
|
||||
if $2 is 'in'
|
||||
new OpNode '!', new InNode $1, $3
|
||||
o "Expression RELATION Expression", ->
|
||||
if $2.charAt(0) is '!'
|
||||
if $2 is '!in'
|
||||
new OpNode '!', new InNode $1, $3
|
||||
else
|
||||
new OpNode '!', new ParentheticalNode new OpNode $2[1..], $1, $3
|
||||
else
|
||||
new OpNode '!', new ParentheticalNode new OpNode $2, $1, $3
|
||||
if $2 is 'in' then new InNode $1, $3 else new OpNode $2, $1, $3
|
||||
]
|
||||
|
||||
|
||||
@@ -583,13 +583,13 @@ operators = [
|
||||
["left", '+', '-']
|
||||
["left", 'SHIFT']
|
||||
["left", 'COMPARE']
|
||||
["left", 'INSTANCEOF', 'NOT_RELATED']
|
||||
["left", 'RELATION']
|
||||
["left", '==', '!=']
|
||||
["left", 'LOGIC']
|
||||
["right", 'COMPOUND_ASSIGN']
|
||||
["left", '.']
|
||||
["nonassoc", 'INDENT', 'OUTDENT']
|
||||
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
|
||||
["right", 'WHEN', 'LEADING_WHEN', 'FORIN', 'FOROF', 'BY', 'THROW']
|
||||
["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS']
|
||||
["right", '=', ':', 'RETURN']
|
||||
["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']
|
||||
|
||||
@@ -41,6 +41,7 @@ exports.Lexer = class Lexer
|
||||
@indent = 0 # The current indentation level.
|
||||
@indebt = 0 # The over-indentation at the current level.
|
||||
@outdebt = 0 # The under-outdentation at the current level.
|
||||
@seenFor = no # The flag for distinguishing FORIN/FOROF from IN/OF.
|
||||
@indents = [] # The stack of all current indentation levels.
|
||||
@tokens = [] # Stream of parsed tokens in the form ['TYPE', value, line]
|
||||
# At every position, run through this list of attempted matches,
|
||||
@@ -84,11 +85,19 @@ exports.Lexer = class Lexer
|
||||
tag = id.toUpperCase()
|
||||
if tag is 'WHEN' and include LINE_BREAK, @tag()
|
||||
tag = 'LEADING_WHEN'
|
||||
else if tag is 'FOR'
|
||||
@seenFor = yes
|
||||
else if include UNARY, tag
|
||||
tag = 'UNARY'
|
||||
else if include(RELATION, tag) and @value() in ['not', '!']
|
||||
@tokens.pop()
|
||||
tag = 'NOT_RELATED'
|
||||
else if include RELATION, tag
|
||||
if tag isnt 'INSTANCEOF' and @seenFor
|
||||
@seenFor = no
|
||||
tag = 'FOR' + tag
|
||||
else
|
||||
tag = 'RELATION'
|
||||
if @value() is '!'
|
||||
@tokens.pop()
|
||||
id = '!' + id
|
||||
if include JS_FORBIDDEN, id
|
||||
if forcedIdentifier
|
||||
tag = 'STRING'
|
||||
@@ -100,7 +109,7 @@ exports.Lexer = class Lexer
|
||||
else if include(RESERVED, id)
|
||||
@identifierError id
|
||||
unless forcedIdentifier
|
||||
tag = id = CONVERSIONS[id] if include COFFEE_ALIASES, id
|
||||
tag = id = COFFEE_ALIASES[id] if COFFEE_ALIASES.hasOwnProperty id
|
||||
if id is '!'
|
||||
tag = 'UNARY'
|
||||
else if include LOGIC, id
|
||||
@@ -307,7 +316,7 @@ exports.Lexer = class Lexer
|
||||
if pval in ['or', 'and']
|
||||
prev = last @tokens
|
||||
prev[0] = 'COMPOUND_ASSIGN'
|
||||
prev[1] = CONVERSIONS[pval] + '='
|
||||
prev[1] = COFFEE_ALIASES[pval] + '='
|
||||
return true
|
||||
if ';' is value then tag = 'TERMINATOR'
|
||||
else if include LOGIC , value then tag = 'LOGIC'
|
||||
@@ -511,14 +520,20 @@ JS_KEYWORDS = [
|
||||
'this', 'null', 'debugger'
|
||||
]
|
||||
|
||||
# CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
|
||||
# be used standalone, but you can reference them as an attached property.
|
||||
COFFEE_ALIASES = ['and', 'or', 'is', 'isnt', 'not']
|
||||
COFFEE_KEYWORDS = COFFEE_ALIASES.concat [
|
||||
# CoffeeScript-only keywords.
|
||||
COFFEE_KEYWORDS = [
|
||||
'then', 'unless', 'until', 'loop'
|
||||
'yes', 'no', 'on', 'off'
|
||||
'of', 'by', 'when'
|
||||
]
|
||||
COFFEE_ALIASES =
|
||||
and : '&&'
|
||||
or : '||'
|
||||
is : '=='
|
||||
isnt : '!='
|
||||
not : '!'
|
||||
COFFEE_KEYWORDS.push op for all op of COFFEE_ALIASES
|
||||
COFFEE_ALIASES['==='] = '=='
|
||||
|
||||
# The list of keywords that are reserved by JavaScript, but not used, or are
|
||||
# used by CoffeeScript internally. We throw an error when these are encountered,
|
||||
@@ -611,12 +626,3 @@ CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', ':
|
||||
# occurs at the start of a line. We disambiguate these from trailing whens to
|
||||
# avoid an ambiguity in the grammar.
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
|
||||
|
||||
# Conversions from CoffeeScript operators into JavaScript ones.
|
||||
CONVERSIONS =
|
||||
'and': '&&'
|
||||
'or': '||'
|
||||
'is': '=='
|
||||
'isnt': '!='
|
||||
'not': '!'
|
||||
'===': '=='
|
||||
|
||||
@@ -102,8 +102,8 @@ class exports.Rewriter
|
||||
# calls that close on the same line, just before their outdent.
|
||||
closeOpenCalls: ->
|
||||
condition = (token, i) ->
|
||||
(token[0] in [')', 'CALL_END']) or
|
||||
token[0] is 'OUTDENT' and @tag(i - 1) is ')'
|
||||
token[0] in [')', 'CALL_END'] or
|
||||
token[0] is 'OUTDENT' and @tag(i - 1) is ')'
|
||||
action = (token, i) ->
|
||||
@tokens[if token[0] is 'OUTDENT' then i - 1 else i][0] = 'CALL_END'
|
||||
@scanTokens (token, i) ->
|
||||
@@ -127,8 +127,8 @@ class exports.Rewriter
|
||||
return false if 'HERECOMMENT' in [@tag(i + 1), @tag(i - 1)]
|
||||
[one, two, three] = @tokens.slice i + 1, i + 4
|
||||
[tag] = token
|
||||
(tag in ['TERMINATOR', 'OUTDENT']) and not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':') or
|
||||
tag is ',' and (one?[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT'])
|
||||
tag in ['TERMINATOR', 'OUTDENT'] and not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':') or
|
||||
tag is ',' and one?[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']
|
||||
action = (token, i) -> @tokens.splice i, 0, ['}', '}', token[2]]
|
||||
@scanTokens (token, i, tokens) ->
|
||||
if include EXPRESSION_START, tag = token[0]
|
||||
@@ -170,7 +170,7 @@ class exports.Rewriter
|
||||
token.call = yes if prev and not prev.spaced and tag is '?'
|
||||
if callObject or
|
||||
prev and prev.spaced and (prev.call or include(IMPLICIT_FUNC, prev[0])) and include(IMPLICIT_CALL, tag) and
|
||||
not (tag is 'UNARY' and (@tag(i + 1) in ['IN', 'OF', 'INSTANCEOF']))
|
||||
not (tag is 'UNARY' and @tag(i + 1) in ['IN', 'OF', 'INSTANCEOF'])
|
||||
tokens.splice i, 0, ['CALL_START', '(', token[2]]
|
||||
condition = (token, i) ->
|
||||
return yes if not seenSingle and token.fromThen
|
||||
@@ -196,7 +196,7 @@ class exports.Rewriter
|
||||
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
|
||||
tokens.splice i, 0, @indentation(token)...
|
||||
return 2
|
||||
if tag is 'CATCH' and (@tag(i + 2) in ['TERMINATOR', 'FINALLY'])
|
||||
if tag is 'CATCH' and @tag(i + 2) in ['TERMINATOR', 'FINALLY']
|
||||
tokens.splice i + 2, 0, @indentation(token)...
|
||||
return 4
|
||||
if include(SINGLE_LINERS, tag) and @tag(i + 1) isnt 'INDENT' and
|
||||
|
||||
Reference in New Issue
Block a user