Class: CTioga2::Commands::InterpreterString::LexicalAnalyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/ctioga2/commands/strings.rb

Overview

A small lexical parser. It’s job is to carry out the function of InterpreterString.parse_until_unquoted.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io, term) ⇒ LexicalAnalyzer

Initializes the parser.



73
74
75
76
# File 'lib/ctioga2/commands/strings.rb', line 73

def initialize(io, term)
  @io = io
  @term = term
end

Instance Attribute Details

#current_stringObject

The current object on the way of being parsed



64
65
66
# File 'lib/ctioga2/commands/strings.rb', line 64

def current_string
  @current_string
end

#ioObject

The io device with which the parser is interacting.



67
68
69
# File 'lib/ctioga2/commands/strings.rb', line 67

def io
  @io
end

#parsedObject

The current string result, as described in InterpreterString



61
62
63
# File 'lib/ctioga2/commands/strings.rb', line 61

def parsed
  @parsed
end

#stateObject

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



58
59
60
# File 'lib/ctioga2/commands/strings.rb', line 58

def state
  @state
end

#termObject

The terminating element



70
71
72
# File 'lib/ctioga2/commands/strings.rb', line 70

def term
  @term
end

Instance Method Details

#parse(eoerror = true) ⇒ Object

Parse the string from the io object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# 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

#push_current_elementObject

Pushes the element currently being parsed unto the result



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# 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