lib/tcl/ruby/parser.rb in tcl-ruby-0.1.0 vs lib/tcl/ruby/parser.rb in tcl-ruby-0.1.1
- old
+ new
@@ -1,71 +1,103 @@
require 'strscan'
module Tcl
module Ruby
class Interpreter
+ EX_CHAR_CHECK = lambda do |s, t|
+ (t && !s.check(/\s|\z/)) || (!t && !s.check(/\s|\z|;/))
+ end.curry
+
def parse(str, to_list = false)
- s = StringScanner.new(str)
- r = ListArray.new
- pdepth = ddepth = bdepth = 0
- buffer = ''
- ret = nil
- until s.empty?
- if s.scan(/\\./m)
- buffer << s[0] unless s[0][1] =~ /\s/
- elsif !to_list && s.scan(/\r\n|\r|\n|;/)
- if pdepth == 0 && ddepth == 0 && bdepth == 0
- r << buffer
- ret = command(r) unless r.empty?
- r = ListArray.new
- else
- buffer << s[0]
- end
- elsif s.scan(/\s+/)
- if pdepth == 0 && ddepth == 0 && bdepth == 0
- r << buffer
- else
- buffer << s[0]
- end
- else
- buffer <<
- if s.scan(/{/)
- # pdepth += 1 if ddepth == 0
- pdepth += 1 if buffer == '' || pdepth != 0
- s[0]
- elsif s.scan(/}/)
- ret = s[0] # pdepth -= 1 if ddepth == 0
- pdepth -= 1 if pdepth != 0
- raise(ParseError, 'extra characters after close-brace') if
- buffer[0] == '{' && pdepth == 0 && !s.check(/\s|\z/)
- ret
- elsif !to_list && s.scan(/\[/)
- bdepth += 1 if pdepth == 0
- s[0]
- elsif !to_list && s.scan(/\]/)
- bdepth -= 1 if pdepth == 0
- s[0]
- elsif s.scan(/"/)
- ret = s[0]
- ddepth = 1 - ddepth if buffer == '' || buffer[0] == '"'
- raise(ParseError, 'extra characters after close-quote') if
- buffer[0] == '"' && ddepth == 0 && !s.check(/\s|\z/)
- ret
- elsif s.scan(/\S/)
- s[0]
- else
- raise(ParseError, "parse error #{s.rest}")
- end
+ str2 = (str + "\n").gsub(/\\\n\s*/, ' ') # replace \ & \n & extra ws
+ @s = StringScanner.new(str2)
+ @list_array = ListArray.new
+ @pstack = [] # stack for brace, bracket, quote
+ @buffer = '' # ListElement.new('')'' # buffer for list element
+ @commands = []
+ until @s.empty?
+ if @s.scan(/\\./) then @buffer << @s[0]
+ elsif @s.scan(/\#/) then parse_comments
+ elsif !to_list && @s.scan(/\r\n|\r|\n|;/) then parse_command_ends
+ elsif @s.scan(/\s/) then parse_blanks
+ elsif @s.scan(/\S/)
+ @buffer << @s[0]
+ analyze_parentheses(to_list, EX_CHAR_CHECK[@s]) if
+ @buffer.parenthesis?
end
end
- r << buffer
- raise(ParseError, 'unmatched parenthesises') if
- ddepth != 0 || pdepth != 0 || bdepth != 0
- if to_list
- r.to_string
+ check_pstack
+ to_list ? @list_array.to_string : command(@commands)
+ end
+
+ private
+
+ def check_pstack
+ raise(ParseError, "unmatched #{@pstack.last}s") if @pstack.any?
+ end
+
+ def parse_command_ends
+ bl = @s[0]
+ if @pstack.empty?
+ @list_array << @buffer
+ @list_array.to_string
+ @commands << @list_array.dup
+ @list_array.clear
else
- ret = command(r) unless r.empty?
- ret
+ @buffer << bl
+ end
+ end
+
+ def parse_blanks
+ @pstack.empty? ? @list_array << @buffer : @buffer << @s[0]
+ end
+
+ def parse_comments
+ if @buffer.empty? && @list_array.empty?
+ @s.scan(/.+$/)
+ else
+ @buffer << @s[0]
+ end
+ end
+
+ def matched_parentheses(id, has_ex_char)
+ if @pstack.last == id
+ r = @pstack.pop
+ if @pstack.empty? && has_ex_char
+ raise(ParseError, "extra characters after close-#{id}")
+ end
+ r
+ end
+ end
+
+ def analyze_braces(to_list, extra_characters_check)
+ if @buffer[-1] == '{'
+ @pstack.push(:brace) if @pstack.last != :quote
+ else
+ matched_parentheses(:brace, extra_characters_check[to_list])
+ end
+ end
+
+ def analyze_quotes(to_list, extra_characters_check)
+ unless matched_parentheses(:quote, extra_characters_check[to_list])
+ @pstack.push :quote if @pstack.last != :brace
+ end
+ end
+
+ def analyze_brackets
+ if @buffer[-1] == '[' && @pstack.last != :brace
+ @pstack.push :bracket
+ elsif @pstack.last == :bracket
+ @pstack.pop
+ end
+ end
+
+ def analyze_parentheses(to_list, extra_characters_check)
+ bl = @buffer[-1]
+ case bl
+ when '{', '}' then analyze_braces(to_list, extra_characters_check)
+ when '[', ']' then analyze_brackets unless to_list
+ when '"' then analyze_quotes(to_list, extra_characters_check)
end
end
end
end
end