~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
183
184
185
186
187
188
# file.rb: a file parser for ctioga2
# copyright (c) 2009 by Vincent Fourmond
  
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
  
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details (in the COPYING file).

require 'stringio'
require 'ctioga2/utils'
require 'ctioga2/log'
require 'ctioga2/commands/commands'
require 'ctioga2/commands/strings'

module CTioga2

  Version::register_svn_info('$Revision: 151 $', '$Date: 2010-06-19 23:45:20 +0200 (Sat, 19 Jun 2010) $')

  module Commands

    module Parsers

      # Raised when EOF is encountered during a symbol parsing
      class UnterminatedSymbol < Exception
      end

      # Unexepected character.
      class UnexpectedCharacter < Exception
      end

      # Syntax error
      class ParserSyntaxError < Exception
      end

      # This class is in charge of parsing a command-line against a list
      # of known commands. 
      class FileParser

        include Log

        # Runs a command file targeting the given _interpreter_.
        def self.run_command_file(file, interpreter)
          FileParser.new.run_command_file(file, interpreter)
        end

        # Runs the given command strings
        def self.run_commands(strings, interpreter)
          FileParser.new.run_commands(strings, interpreter)
        end

        # Runs a command file targeting the given _interpreter_.
        def run_command_file(file, interpreter)
          f = open(file)
          parse_io_object(f, interpreter)
        end

        # Runs the given command strings
        def run_commands(strings, interpreter)
          io = StringIO.new(strings)
          parse_io_object(io, interpreter)
        end

        # Parses a given _io_ object, sending commands/variable
        # definitions to the given _interpreter_.
        def parse_io_object(io, interpreter)
          # The process is simple: we look for symbols and
          # corresponding syntax element: parentheses or assignments
          while(1)
            symbol = up_to_next_symbol(io)
            break if not symbol
            
            while(1)
              c = io.getc
              if ! c              # EOF
                raise ParserSyntaxError, "Expecting something after symbol #{symbol}"
              end
              ch = c.chr
              if ch =~ /\s/      # blank...
                next
              elsif ch == '('    # beginning of a function call
                # Parse string:
                str = InterpreterString.parse_until_unquoted(io,")")
                # Now, we need to split str.
                args = str.expand_and_split(/\s*,\s*/, interpreter)

                cmd = interpreter.get_command(symbol)
                real_args = args.slice!(0, cmd.argument_number)
                # And now the options:
                options = {}

                # Problem: the space on the right of the = sign is
                # *significant*. 
                for o in args
                  if o =~ /^\s*\/?([\w-]+)\s*=(.*)/
                    if cmd.has_option? $1
                      options[$1] = $2
                    else
                      error { 
                        "Command #{cmd.name} does not take option #{$1}" 
                      }
                    end
                  end
                end

                interpreter.run_command(cmd, real_args, options)
                io.getc         # Slurp up the )
                break
              elsif ch == ':'   # Assignment
                c = io.getc
                if ! c          # EOF
                  raise ParserSyntaxError, "Expecting = after :"
                end
                ch = c.chr
                if ch != '='
                  raise ParserSyntaxError, "Expecting = after :"
                end
                str = InterpreterString.parse_until_unquoted(io,"\n", false)
                interpreter.variables.define_variable(symbol, str, 
                                                      interpreter)
                break
              elsif ch == '='
                str = InterpreterString.parse_until_unquoted(io,"\n", false)
                interpreter.variables.define_variable(symbol, str, nil) 
                break
              else
                raise UnexpectedCharacter, "Did not expect #{ch} after #{symbol}"
              end
            end
          end
        end

        protected

        SYMBOL_CHAR_REGEX = /[a-zA-Z0-9_-]/
        
        # Parses the _io_ stream up to and including the next
        # symbol. Only white space or comments may be found on the
        # way. This function returns the symbol.
        #
        # Symbols are composed of the alphabet SYMBOL_CHAR_REGEX.
        def up_to_next_symbol(io)

          symbol = nil          # As long as no symbol as been started
          # it will stay nil.
          while(1)
            c = io.getc
            if ! c              # EOF
              if symbol
                raise UnterminatedSymbol, "EOF reached during symbol parsing"
              else
                # File is finished and we didn't meet any symbol.
                # Nothing to do !
                return nil
              end
            end
            ch = c.chr
            if symbol           # We have started
              if ch =~ SYMBOL_CHAR_REGEX
                symbol += ch
              else
                io.ungetc(c)
                return symbol
              end
            else
              if ch =~ SYMBOL_CHAR_REGEX
                symbol = ch
              elsif ch =~ /\s/
                # Nothing
              elsif ch == '#'
                io.gets
              else
                raise UnexpectedCharacter, "Unexpected character: #{ch}, when looking for a symbol"
              end
            end
          end
        end

      end

    end
  end
end