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
|