class CTioga2::Commands::InterpreterString::LexicalAnalyzer
A small lexical parser. It's job is to carry out the function of InterpreterString.parse_until_unquoted
.
Attributes
The current object on the way of being parsed
The io device with which the parser is interacting.
The current string result, as described in InterpreterString
The state of the parser:
-
:start -> first starting elements
-
:top -> toplevel
-
:single -> in a single quoted string
-
:double -> in a double quoted string
-
:dollar -> last element was a dollar at top-level
-
:dq_dollar -> last element was an unescaped dollar within a double quoted string
-
:escape -> last element was an unescaped escape char within a double-quoted string.
-
:var -> in a $(variable)
-
:dq_var -> in a $(variable) within a double-quoted string
The terminating element
Public Class Methods
Initializes the parser.
# File lib/ctioga2/commands/strings.rb, line 73 def initialize(io, term) @io = io @term = term end
Public Instance Methods
Parse the string from the io object
# File lib/ctioga2/commands/strings.rb, line 79 def parse(eoerror = true) @state = :start @parsed = [] @current_string = '' i = -1 while(1) c = @io.getc if ! c # EOF if eoerror raise UnterminatedString, "EOF reached before the end of this string" else push_current_element return @parsed end end # Convert the integer to a string. ch = c.chr i += 1 if (@state == :start || @state == :top) and (term.include?(ch)) # Finished push_current_element @io.ungetc(c) # We push back the last char. return @parsed end # puts "#{@state.inspect} -- #{ch}" # We skip white space at the beginning of the string. if @state == :start # Skip white space if ! (ch =~ /\s/) @state = :top end end case @state when :escape # Evaluating escape chars @current_string += eval("\"\\#{ch}\"") @state = :double when :dollar, :dq_dollar @state = (@state == :dollar ? :top : :double) if ch == '(' # Beginning of a variable within a # quoted string push_current_element @state = (@state == :top ? :var : :dq_var) else @current_string += "$#{ch}" end when :single # The simplest string if ch == "'" # End of string push_current_element @state = :top else @current_string += ch end when :var, :dq_var if ch == ")" push_current_element @state = (@state == :var ? :top : :double) elsif ch =~ /\s/ # We don't have a variable, but a function... @accessory = InterpreterString.parse_until_unquoted(@io, ")", true) ch = @io.getc # Slurp the closing ) ns = (@state == :var ? :top : :double) @state = (:var ? :funcall : :dq_funcall) push_current_element @state = ns ## @todo Optional: instead of having a space, use a , ## or . or # to signify different separators ? (but ## quoting makes this more-or-less unnecessary, hey ?) else @current_string += ch end when :top if ch == "'" # We start a single-quoted string push_current_element @state = :single elsif ch == '$' # Dollar state @state = :dollar elsif ch == '"' push_current_element @state = :double elsif ch == '#' # A comment: we read until end-of-line @io.gets # and ignore the results else @current_string += ch end when :double if ch == '"' # (necessarily unquoted) push_current_element @state = :top elsif ch == '$' @state = :dq_dollar elsif ch == "\\" @state = :escape else @current_string += ch end end end end
Pushes the element currently being parsed unto the result
# File lib/ctioga2/commands/strings.rb, line 186 def push_current_element if @current_string.size == 0 return end case @state when :top # We push an unquoted string @parsed << [:unquoted, @current_string] when :single, :double @parsed << [:quoted, @current_string] when :var @parsed << [:unquoted_variable, @current_string] when :dq_var @parsed << [:quoted_variable, @current_string] when :funcall @parsed << [:unquoted_funcall, @current_string, @accessory] when :dq_funcall @parsed << [:quoted_funcall, @current_string, @accessory] when :dollar @parsed << [:unquoted, @current_string + '$'] when :dq_dollar @parsed << [:quoted, @current_string + '$'] when :escape @parsed << [:quoted, @current_string + "\\" ] when :start # Empty string at the beginning. Nothing interesting, move # along ! else raise "Fatal bug of the lexical analyzer here : unkown state" end # Flush current string @current_string = "" end