mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
balancing parens closing single-line blocks
This commit is contained in:
parent
ed1c1f7a0e
commit
e6cd32f2fc
1 changed files with 21 additions and 5 deletions
|
@ -48,7 +48,7 @@ module CoffeeScript
|
||||||
# Single-line flavors of block expressions that have unclosed endings.
|
# Single-line flavors of block expressions that have unclosed endings.
|
||||||
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||||
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN]
|
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN]
|
||||||
SINGLE_CLOSERS = ["\n", ")", :CATCH, :FINALLY, :ELSE, :OUTDENT]
|
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT]
|
||||||
|
|
||||||
# The inverse mappings of token pairs we're trying to fix up.
|
# The inverse mappings of token pairs we're trying to fix up.
|
||||||
INVERSES = {:INDENT => :OUTDENT, :OUTDENT => :INDENT, '(' => ')', ')' => '('}
|
INVERSES = {:INDENT => :OUTDENT, :OUTDENT => :INDENT, '(' => ')', ')' => '('}
|
||||||
|
@ -252,6 +252,7 @@ module CoffeeScript
|
||||||
# Because our grammar is LALR(1), it can't handle some single-line
|
# Because our grammar is LALR(1), it can't handle some single-line
|
||||||
# expressions that lack ending delimiters. Use the lexer to add the implicit
|
# expressions that lack ending delimiters. Use the lexer to add the implicit
|
||||||
# blocks, so it doesn't need to.
|
# blocks, so it doesn't need to.
|
||||||
|
# ')' can close a single-line block, but we need to make sure it's balanced.
|
||||||
def add_implicit_indentation
|
def add_implicit_indentation
|
||||||
scan_tokens do |prev, token, post, i|
|
scan_tokens do |prev, token, post, i|
|
||||||
if SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT &&
|
if SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT &&
|
||||||
|
@ -259,12 +260,17 @@ module CoffeeScript
|
||||||
line = token[1].line
|
line = token[1].line
|
||||||
@tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
|
@tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
|
||||||
idx = i + 1
|
idx = i + 1
|
||||||
|
parens = 0
|
||||||
loop do
|
loop do
|
||||||
idx += 1
|
idx += 1
|
||||||
if !@tokens[idx] || SINGLE_CLOSERS.include?(@tokens[idx][0])
|
tok = @tokens[idx]
|
||||||
|
if !tok || SINGLE_CLOSERS.include?(tok[0]) ||
|
||||||
|
(tok[0] == ')' && parens == 0)
|
||||||
@tokens.insert(idx, [:OUTDENT, Value.new(2, line)])
|
@tokens.insert(idx, [:OUTDENT, Value.new(2, line)])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
parens += 1 if tok[0] == '('
|
||||||
|
parens -= 1 if tok[0] == ')'
|
||||||
end
|
end
|
||||||
@tokens.delete_at(i) if token[0] == :THEN
|
@tokens.delete_at(i) if token[0] == :THEN
|
||||||
end
|
end
|
||||||
|
@ -301,24 +307,34 @@ module CoffeeScript
|
||||||
# up balanced in the end.
|
# up balanced in the end.
|
||||||
#
|
#
|
||||||
def rewrite_closing_parens
|
def rewrite_closing_parens
|
||||||
|
verbose = ENV['VERBOSE']
|
||||||
stack, debt = [], Hash.new(0)
|
stack, debt = [], Hash.new(0)
|
||||||
|
stack_stats = lambda { "stack: #{stack.inspect} debt: #{debt.inspect}" }
|
||||||
|
puts "original stream: #{@tokens.inspect}" if verbose
|
||||||
scan_tokens do |prev, token, post, i|
|
scan_tokens do |prev, token, post, i|
|
||||||
tag, inv = token[0], INVERSES[token[0]]
|
tag, inv = token[0], INVERSES[token[0]]
|
||||||
stack.push(token) if [:INDENT, '('].include?(tag)
|
if [:INDENT, '('].include?(tag)
|
||||||
if [:OUTDENT, ')'].include?(tag)
|
stack.push(token)
|
||||||
|
puts "pushing #{tag} #{stack_stats[]}" if verbose
|
||||||
|
elsif [:OUTDENT, ')'].include?(tag)
|
||||||
# If the tag is already in our debt, swallow it.
|
# If the tag is already in our debt, swallow it.
|
||||||
if debt[inv] > 0
|
if debt[inv] > 0
|
||||||
debt[inv] -= 1
|
debt[inv] -= 1
|
||||||
@tokens.delete_at(i)
|
@tokens.delete_at(i)
|
||||||
|
puts "tag in debt #{tag} #{stack_stats[]}" if verbose
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
# Pop the stack of open delimiters.
|
# Pop the stack of open delimiters.
|
||||||
match = stack.pop
|
match = stack.pop
|
||||||
mtag = match[0]
|
mtag = match[0]
|
||||||
# Continue onwards if it's the expected tag.
|
# Continue onwards if it's the expected tag.
|
||||||
next if tag == INVERSES[mtag]
|
if tag == INVERSES[mtag]
|
||||||
|
puts "expected tag #{tag} #{stack_stats[]}" if verbose
|
||||||
|
next
|
||||||
|
end
|
||||||
# Unexpected close, insert correct close, adding to the debt.
|
# Unexpected close, insert correct close, adding to the debt.
|
||||||
debt[mtag] += 1
|
debt[mtag] += 1
|
||||||
|
puts "unexpected #{tag}, replacing with #{INVERSES[mtag]} #{stack_stats[]}" if verbose
|
||||||
val = mtag == :INDENT ? match[1] : ')'
|
val = mtag == :INDENT ? match[1] : ')'
|
||||||
@tokens.insert(i, [INVERSES[mtag], Value.new(val, token[1].line)])
|
@tokens.insert(i, [INVERSES[mtag], Value.new(val, token[1].line)])
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue