ctioga2 0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. data/COPYING +339 -0
  2. data/Changelog +6 -0
  3. data/bin/ctioga2 +26 -0
  4. data/lib/ctioga2/commands/arguments.rb +58 -0
  5. data/lib/ctioga2/commands/commands.rb +258 -0
  6. data/lib/ctioga2/commands/doc/doc.rb +118 -0
  7. data/lib/ctioga2/commands/doc/documentation-commands.rb +119 -0
  8. data/lib/ctioga2/commands/doc/help.rb +95 -0
  9. data/lib/ctioga2/commands/doc/html.rb +230 -0
  10. data/lib/ctioga2/commands/doc/introspection.rb +211 -0
  11. data/lib/ctioga2/commands/doc/man.rb +279 -0
  12. data/lib/ctioga2/commands/doc/markup.rb +359 -0
  13. data/lib/ctioga2/commands/general-commands.rb +119 -0
  14. data/lib/ctioga2/commands/general-types.rb +118 -0
  15. data/lib/ctioga2/commands/groups.rb +73 -0
  16. data/lib/ctioga2/commands/interpreter.rb +257 -0
  17. data/lib/ctioga2/commands/parsers/command-line.rb +187 -0
  18. data/lib/ctioga2/commands/parsers/file.rb +186 -0
  19. data/lib/ctioga2/commands/strings.rb +303 -0
  20. data/lib/ctioga2/commands/type.rb +100 -0
  21. data/lib/ctioga2/commands/variables.rb +101 -0
  22. data/lib/ctioga2/data/backends/backend.rb +260 -0
  23. data/lib/ctioga2/data/backends/backends.rb +39 -0
  24. data/lib/ctioga2/data/backends/backends/gnuplot.rb +140 -0
  25. data/lib/ctioga2/data/backends/backends/math.rb +121 -0
  26. data/lib/ctioga2/data/backends/backends/text.rb +335 -0
  27. data/lib/ctioga2/data/backends/description.rb +405 -0
  28. data/lib/ctioga2/data/backends/factory.rb +73 -0
  29. data/lib/ctioga2/data/backends/parameter.rb +109 -0
  30. data/lib/ctioga2/data/datacolumn.rb +245 -0
  31. data/lib/ctioga2/data/dataset.rb +233 -0
  32. data/lib/ctioga2/data/filters.rb +131 -0
  33. data/lib/ctioga2/data/merge.rb +43 -0
  34. data/lib/ctioga2/data/point.rb +72 -0
  35. data/lib/ctioga2/data/stack.rb +294 -0
  36. data/lib/ctioga2/graphics/coordinates.rb +73 -0
  37. data/lib/ctioga2/graphics/elements.rb +111 -0
  38. data/lib/ctioga2/graphics/elements/containers.rb +111 -0
  39. data/lib/ctioga2/graphics/elements/curve2d.rb +155 -0
  40. data/lib/ctioga2/graphics/elements/element.rb +90 -0
  41. data/lib/ctioga2/graphics/elements/primitive.rb +256 -0
  42. data/lib/ctioga2/graphics/elements/subplot.rb +140 -0
  43. data/lib/ctioga2/graphics/generator.rb +68 -0
  44. data/lib/ctioga2/graphics/legends.rb +108 -0
  45. data/lib/ctioga2/graphics/legends/area.rb +199 -0
  46. data/lib/ctioga2/graphics/legends/items.rb +183 -0
  47. data/lib/ctioga2/graphics/legends/provider.rb +58 -0
  48. data/lib/ctioga2/graphics/legends/storage.rb +65 -0
  49. data/lib/ctioga2/graphics/root.rb +209 -0
  50. data/lib/ctioga2/graphics/styles.rb +30 -0
  51. data/lib/ctioga2/graphics/styles/axes.rb +247 -0
  52. data/lib/ctioga2/graphics/styles/background.rb +122 -0
  53. data/lib/ctioga2/graphics/styles/base.rb +115 -0
  54. data/lib/ctioga2/graphics/styles/carrays.rb +53 -0
  55. data/lib/ctioga2/graphics/styles/curve.rb +101 -0
  56. data/lib/ctioga2/graphics/styles/drawable.rb +87 -0
  57. data/lib/ctioga2/graphics/styles/factory.rb +351 -0
  58. data/lib/ctioga2/graphics/styles/legend.rb +63 -0
  59. data/lib/ctioga2/graphics/styles/plot.rb +410 -0
  60. data/lib/ctioga2/graphics/styles/sets.rb +64 -0
  61. data/lib/ctioga2/graphics/styles/texts.rb +277 -0
  62. data/lib/ctioga2/graphics/subplot-commands.rb +141 -0
  63. data/lib/ctioga2/graphics/types.rb +188 -0
  64. data/lib/ctioga2/graphics/types/bijection.rb +79 -0
  65. data/lib/ctioga2/graphics/types/boundaries.rb +170 -0
  66. data/lib/ctioga2/graphics/types/boxes.rb +157 -0
  67. data/lib/ctioga2/graphics/types/dimensions.rb +157 -0
  68. data/lib/ctioga2/graphics/types/point.rb +247 -0
  69. data/lib/ctioga2/log.rb +97 -0
  70. data/lib/ctioga2/metabuilder/type.rb +316 -0
  71. data/lib/ctioga2/metabuilder/types.rb +39 -0
  72. data/lib/ctioga2/metabuilder/types/coordinates.rb +124 -0
  73. data/lib/ctioga2/metabuilder/types/dates.rb +43 -0
  74. data/lib/ctioga2/metabuilder/types/lists.rb +188 -0
  75. data/lib/ctioga2/metabuilder/types/numbers.rb +97 -0
  76. data/lib/ctioga2/metabuilder/types/strings.rb +93 -0
  77. data/lib/ctioga2/metabuilder/types/styles.rb +178 -0
  78. data/lib/ctioga2/plotmaker.rb +677 -0
  79. data/lib/ctioga2/postprocess.rb +115 -0
  80. data/lib/ctioga2/utils.rb +120 -0
  81. data/setup.rb +1586 -0
  82. metadata +144 -0
@@ -0,0 +1,187 @@
1
+ # command-line.rb: a command-line parser for ctioga2
2
+ # copyright (c) 2009 by Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'ctioga2/utils'
15
+ require 'ctioga2/log'
16
+ require 'ctioga2/commands/commands'
17
+
18
+ module CTioga2
19
+
20
+ Version::register_svn_info('$Revision: 2 $', '$Date: 2009-04-25 14:03:30 +0200 (Sat, 25 Apr 2009) $')
21
+
22
+ module Commands
23
+
24
+ # In this modules are classes that parse a source of commands, and
25
+ # yield to the caller the commands (Command) along with their
26
+ # unprocessed arguments.
27
+ module Parsers
28
+
29
+ # An exception raised upon redefinition of a short or long
30
+ # option.
31
+ class OptionRedefined < Exception
32
+ end
33
+
34
+ # An exception raised when the parser encounters an unkown
35
+ # option.
36
+ class OptionUnkown < Exception
37
+ end
38
+
39
+ # This class is in charge of parsing a command-line against a
40
+ # list of known commands.
41
+ class CommandLineParser
42
+ include Log
43
+
44
+ # A hash 'short-option-letter' => [number of args, Command]
45
+ attr_reader :short_options
46
+
47
+ # A hash 'long-option-name' => [number of args, Command]
48
+ attr_reader :long_options
49
+
50
+ # The list of commands
51
+ attr_reader :commands
52
+
53
+ # A [number of args, Command] for the default command, ie
54
+ # the one that applies on non-command files.
55
+ attr_reader :default_command
56
+
57
+ # Creates a CommandLineParser that will understand the
58
+ # given _commands_
59
+ def initialize(commands, default = nil)
60
+ @commands = commands
61
+ prepare_option_hashes(default)
62
+ end
63
+
64
+
65
+ # Takes an _argv_ array representing the command-line and a
66
+ # target _intepreter_, and runs the commands found on the
67
+ # command line. Yields arguments which are not part of a
68
+ # command, or feed them to the #default_command if it was
69
+ # specified.
70
+ def parse_command_line(argv, interpreter)
71
+ # We duplicate the original array
72
+ argv = argv.dup
73
+ options = nil # currently never used.
74
+ while argv.size > 0
75
+ current = argv.shift
76
+ if current =~ /^--(.*)/ # a long option
77
+ if @long_options.key?($1)
78
+ command, arguments, options =
79
+ extract_command_arguments(argv, @long_options[$1])
80
+
81
+ interpreter.run_command(command, arguments, options)
82
+ else
83
+ raise OptionUnkown, "Long option #{current} is not known"
84
+ end
85
+ elsif current =~ /^-(.*)/ # Short options
86
+ # We do the same as above, but splitting into letters first:
87
+ short_options = $1.split('')
88
+ for short in short_options
89
+ if @short_options.key?(short)
90
+ command, arguments, options =
91
+ extract_command_arguments(argv, @short_options[short])
92
+ interpreter.run_command(command, arguments, options)
93
+ else
94
+ raise OptionUnkown, "Short option -#{short} is not known"
95
+ end
96
+ end
97
+ else
98
+ if @default_command
99
+ argv.unshift current
100
+ command, arguments, options =
101
+ extract_command_arguments(argv, @default_command)
102
+ interpreter.run_command(command, arguments, options)
103
+ else
104
+ yield current
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ protected
111
+
112
+ # Prepares the #short_options and #long_options hashes for use
113
+ # in #parse_command_line
114
+ def prepare_option_hashes(default = nil)
115
+ @short_options = {}
116
+ @long_options = {}
117
+ for cmd in @commands
118
+ short = cmd.short_option
119
+ boolean = (cmd.argument_number == 1 &&
120
+ cmd.arguments.first.type.boolean?)
121
+ if short
122
+ if @short_options.key? short
123
+ raise OptionRedefined, "Short option #{short} was already defined as command #{cmd.name}"
124
+ end
125
+ if boolean
126
+ @short_options[short] = [-1, cmd]
127
+ else
128
+ @short_options[short] = [cmd.argument_number, cmd]
129
+ end
130
+ end
131
+ long = cmd.long_option
132
+ if long
133
+ if @long_options.key? short
134
+ raise OptionRedefined, "Long option #{long} was already defined as command #{cmd.name}"
135
+ end
136
+ if boolean
137
+ @long_options[long] = [-1, cmd]
138
+ @long_options["no-#{long}"] = [-2, cmd]
139
+ else
140
+ @long_options[long] = [cmd.argument_number, cmd]
141
+ end
142
+ end
143
+ end
144
+ if default
145
+ @default_command = [default.argument_number, default]
146
+ end
147
+ end
148
+
149
+ # Extract command, arguments and potential options from the
150
+ # given _argv_ array. The second argument is what is stored in
151
+ # the #short_options and #long_options hashes.
152
+ #
153
+ # Returns an array
154
+ # [command, arguments, options]
155
+ def extract_command_arguments(argv, cmd_val)
156
+ number, command = cmd_val
157
+ options = {}
158
+
159
+ # Special case for boolean arguments
160
+ if number < 0
161
+ arguments = [number == -1]
162
+ else
163
+ arguments = argv.slice!(0,number)
164
+ end
165
+
166
+ # We try and go fishing for options, in the form
167
+ # /option=stuff.
168
+ while argv.first =~ /^\/([\w-]+)=(.*)/
169
+ if command.has_option? $1
170
+ options[$1] = $2
171
+ argv.shift
172
+ else
173
+ warn "Argument #{argv.first} looks like an option, but does not match any of the command #{command.name}"
174
+ break
175
+ end
176
+ end
177
+
178
+ return [command, arguments, options]
179
+
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+ end
186
+ end
187
+
@@ -0,0 +1,186 @@
1
+ # file.rb: a file parser for ctioga2
2
+ # copyright (c) 2009 by Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'stringio'
15
+ require 'ctioga2/utils'
16
+ require 'ctioga2/log'
17
+ require 'ctioga2/commands/commands'
18
+ require 'ctioga2/commands/strings'
19
+
20
+ module CTioga2
21
+
22
+ Version::register_svn_info('$Revision: 2 $', '$Date: 2009-04-25 14:03:30 +0200 (Sat, 25 Apr 2009) $')
23
+
24
+ module Commands
25
+
26
+ module Parsers
27
+
28
+ # Raised when EOF is encountered during a symbol parsing
29
+ class UnterminatedSymbol < Exception
30
+ end
31
+
32
+ # Unexepected character.
33
+ class UnexpectedCharacter < Exception
34
+ end
35
+
36
+ # Syntax error
37
+ class ParserSyntaxError < Exception
38
+ end
39
+
40
+ # This class is in charge of parsing a command-line against a list
41
+ # of known commands.
42
+ class FileParser
43
+
44
+ include Log
45
+
46
+ # Runs a command file targeting the given _interpreter_.
47
+ def self.run_command_file(file, interpreter)
48
+ FileParser.new.run_command_file(file, interpreter)
49
+ end
50
+
51
+ # Runs the given command strings
52
+ def self.run_commands(strings, interpreter)
53
+ FileParser.new.run_commands(strings, interpreter)
54
+ end
55
+
56
+ # Runs a command file targeting the given _interpreter_.
57
+ def run_command_file(file, interpreter)
58
+ f = open(file)
59
+ parse_io_object(f, interpreter)
60
+ end
61
+
62
+ # Runs the given command strings
63
+ def run_commands(strings, interpreter)
64
+ io = StringIO.new(strings)
65
+ parse_io_object(io, interpreter)
66
+ end
67
+
68
+ # Parses a given _io_ object, sending commands/variable
69
+ # definitions to the given _interpreter_.
70
+ def parse_io_object(io, interpreter)
71
+ # The process is simple: we look for symbols and
72
+ # corresponding syntax element: parentheses or assignments
73
+ while(1)
74
+ symbol = up_to_next_symbol(io)
75
+ break if not symbol
76
+
77
+ while(1)
78
+ c = io.getc
79
+ if ! c # EOF
80
+ raise ParserSyntaxError, "Expecting something after symbol #{symbol}"
81
+ end
82
+ ch = c.chr
83
+ if ch =~ /\s/ # blank...
84
+ next
85
+ elsif ch == '(' # beginning of a function call
86
+ # Parse string:
87
+ str = InterpreterString.parse_until_unquoted(io,")")
88
+ # Now, we need to split str.
89
+ args = str.expand_and_split(/\s*,\s*/, interpreter)
90
+
91
+ cmd = interpreter.get_command(symbol)
92
+ real_args = args.slice!(0, cmd.argument_number)
93
+ # And now the options:
94
+ options = {}
95
+
96
+ # Problem: the space on the right of the = sign is
97
+ # *significant*.
98
+ for o in args
99
+ if o =~ /^\s*([\w-]+)\s*=(.*)/
100
+ if cmd.has_option? $1
101
+ options[$1] = $2
102
+ else
103
+ error "Command #{cmd.name} does not take option #{$1}"
104
+ end
105
+ end
106
+ end
107
+
108
+ interpreter.run_command(cmd, real_args, options)
109
+ io.getc # Slurp up the )
110
+ break
111
+ elsif ch == ':' # Assignment
112
+ c = io.getc
113
+ if ! c # EOF
114
+ raise ParserSyntaxError, "Expecting = after :"
115
+ end
116
+ ch = c.chr
117
+ if ch != '='
118
+ raise ParserSyntaxError, "Expecting = after :"
119
+ end
120
+ str = InterpreterString.parse_until_unquoted(io,"\n", false)
121
+ interpreter.variables.define_variable(symbol, str,
122
+ interpreter)
123
+ break
124
+ elsif ch == '='
125
+ str = InterpreterString.parse_until_unquoted(io,"\n", false)
126
+ interpreter.variables.define_variable(symbol, str, nil)
127
+ break
128
+ else
129
+ raise UnexpectedCharacter, "Did not expect #{ch} after #{symbol}"
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ protected
136
+
137
+ SYMBOL_CHAR_REGEX = /[a-zA-Z0-9_-]/
138
+
139
+ # Parses the _io_ stream up to and including the next
140
+ # symbol. Only white space or comments may be found on the
141
+ # way. This function returns the symbol.
142
+ #
143
+ # Symbols are composed of the alphabet SYMBOL_CHAR_REGEX.
144
+ def up_to_next_symbol(io)
145
+
146
+ symbol = nil # As long as no symbol as been started
147
+ # it will stay nil.
148
+ while(1)
149
+ c = io.getc
150
+ if ! c # EOF
151
+ if symbol
152
+ raise UnterminatedSymbol, "EOF reached during symbol parsing"
153
+ else
154
+ # File is finished and we didn't meet any symbol.
155
+ # Nothing to do !
156
+ return nil
157
+ end
158
+ end
159
+ ch = c.chr
160
+ if symbol # We have started
161
+ if ch =~ SYMBOL_CHAR_REGEX
162
+ symbol += ch
163
+ else
164
+ io.ungetc(c)
165
+ return symbol
166
+ end
167
+ else
168
+ if ch =~ SYMBOL_CHAR_REGEX
169
+ symbol = ch
170
+ elsif ch =~ /\s/
171
+ # Nothing
172
+ elsif ch == '#'
173
+ io.gets
174
+ else
175
+ raise UnexpectedCharacter, "Unexpected character: #{ch}, when looking for a symbol"
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+ end
185
+ end
186
+
@@ -0,0 +1,303 @@
1
+ # strings.rb: the core of the file-based interpretation: strings !
2
+ # copyright (c) 2009 by Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details (in the COPYING file).
13
+
14
+ require 'ctioga2/utils'
15
+ require 'stringio' # For debugging purposes
16
+
17
+ module CTioga2
18
+
19
+ Version::register_svn_info('$Revision: 2 $', '$Date: 2009-04-25 14:03:30 +0200 (Sat, 25 Apr 2009) $')
20
+
21
+ module Commands
22
+
23
+ class UnterminatedString < Exception
24
+ end
25
+
26
+ # All variables and arguments are only strings. There is no type
27
+ # in ctioga, at least not on the variable/strings level. Ruby
28
+ # functions of course work with typed objects, but all user input
29
+ # is taken to be a string and is converted using the
30
+ # CTioga2::Metabuilder::Type system.
31
+ #
32
+ # A string is however not a simple Ruby String, as it can contain
33
+ # elements that are expanded, and it needs to provide a way to be
34
+ # split into substrings for arguments/options processing.
35
+ #
36
+ # A string is composed of an array of [type, value, ... ? ]
37
+ # arrays, where _type_ can have the following meanings:
38
+ # * :unquoted : an unquoted string (can be split)
39
+ # * :unquoted_variable : an unquoted variable (replacement text
40
+ # can be split)
41
+ # * :quoted : a quoted string (cannot be split)
42
+ # * :quoted_variable : a quoted variable (cannot be split)
43
+ class InterpreterString
44
+
45
+ # A small lexical parser. It's job is to carry out the function
46
+ # of InterpreterString.parse_until_unquoted.
47
+ class LexicalAnalyzer
48
+ # The state of the parser:
49
+ # * :start -> first starting elements
50
+ # * :top -> toplevel
51
+ # * :single -> in a single quoted string
52
+ # * :double -> in a double quoted string
53
+ # * :dollar -> last element was a dollar at top-level
54
+ # * :dq_dollar -> last element was an unescaped dollar within
55
+ # a double quoted string
56
+ # * :escape -> last element was an unescaped escape char
57
+ # within a double-quoted string.
58
+ # * :var -> in a $(variable)
59
+ # * :dq_var -> in a $(variable) within a double-quoted string
60
+ attr_accessor :state
61
+
62
+ # The current string result, as described in InterpreterString
63
+ attr_accessor :parsed
64
+
65
+ # The current object on the way of being parsed
66
+ attr_accessor :current_string
67
+
68
+ # The io device with which the parser is interacting.
69
+ attr_accessor :io
70
+
71
+ # The terminating element
72
+ attr_accessor :term
73
+
74
+ # Initializes the parser.
75
+ def initialize(io, term)
76
+ @io = io
77
+ @term = term
78
+ end
79
+
80
+ # Parse the string from the _io_ object
81
+ def parse(eoerror = true)
82
+ @state = :start
83
+ @parsed = []
84
+ @current_string = ''
85
+
86
+ i = -1
87
+ while(1)
88
+ c = @io.getc
89
+ if ! c # EOF
90
+ if eoerror
91
+ raise UnterminatedString, "EOF reached before the end of this string"
92
+ else
93
+ push_current_element
94
+ return @parsed
95
+ end
96
+ end
97
+ # Convert the integer to a string.
98
+ ch = c.chr
99
+ i += 1
100
+ if (@state == :start || @state == :top) and
101
+ (term.include?(ch)) # Finished
102
+ push_current_element
103
+ @io.ungetc(c) # We push back the last char.
104
+ return @parsed
105
+ end
106
+
107
+ # puts "#{@state.inspect} -- #{ch}"
108
+
109
+ # We skip white space at the beginning of the string.
110
+ if @state == :start
111
+ # Skip white space
112
+ if ! (ch =~ /\s/)
113
+ @state = :top
114
+ end
115
+ end
116
+
117
+ case @state
118
+ when :escape
119
+ # Evaluating escape chars
120
+ @current_string += eval("\"\\#{ch}\"")
121
+ @state = :double
122
+ when :dollar, :dq_dollar
123
+ @state = (@state == :dollar ? :top : :double)
124
+ if ch == '(' # Beginning of a variable within a
125
+ # quoted string
126
+ push_current_element
127
+ @state = (@state == :top ? :var : :dq_var)
128
+ else
129
+ @current_string += "$#{ch}"
130
+ end
131
+ when :single # The simplest string
132
+ if ch == "'" # End of string
133
+ push_current_element
134
+ @state = :top
135
+ else
136
+ @current_string += ch
137
+ end
138
+ when :var, :dq_var
139
+ if ch == ")"
140
+ push_current_element
141
+ @state = (@state == :var ? :top : :double)
142
+ else
143
+ @current_string += ch
144
+ end
145
+ when :top
146
+ if ch == "'" # We start a single-quoted string
147
+ push_current_element
148
+ @state = :single
149
+ elsif ch == '$' # Dollar state
150
+ @state = :dollar
151
+ elsif ch == '"'
152
+ push_current_element
153
+ @state = :double
154
+ elsif ch == '#' # A comment: we read until end-of-line
155
+ @io.gets # and ignore the results
156
+ else
157
+ @current_string += ch
158
+ end
159
+ when :double
160
+ if ch == '"' # (necessarily unquoted)
161
+ push_current_element
162
+ @state = :top
163
+ elsif ch == '$'
164
+ @state = :dq_dollar
165
+ elsif ch == "\\"
166
+ @state = :escape
167
+ else
168
+ @current_string += ch
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+
175
+ # Pushes the element currently being parsed unto the
176
+ # result
177
+ def push_current_element
178
+ if @current_string.size == 0
179
+ return
180
+ end
181
+ case @state
182
+ when :top
183
+ # We push an unquoted string
184
+ @parsed << [:unquoted, @current_string]
185
+ when :single, :double
186
+ @parsed << [:quoted, @current_string]
187
+ when :var
188
+ @parsed << [:unquoted_variable, @current_string]
189
+ when :dq_var
190
+ @parsed << [:quoted_variable, @current_string]
191
+ when :dollar
192
+ @parsed << [:unquoted, @current_string + '$']
193
+ when :dq_dollar
194
+ @parsed << [:quoted, @current_string + '$']
195
+ when :escape
196
+ @parsed << [:quoted, @current_string + "\\" ]
197
+ when :start
198
+ # Empty string at the beginning. Nothing interesting, move
199
+ # along !
200
+ else
201
+ raise "Fatal bug of the lexical analyzer here : unkown state"
202
+ end
203
+ # Flush current string
204
+ @current_string = ""
205
+ end
206
+
207
+ end
208
+
209
+
210
+ # The array of the aforementioned [_type_, _value_, ...] arrays
211
+ attr_accessor :contents
212
+
213
+ # Read the given _io_ stream until an unquoted element of
214
+ # _term_ is found. Returns the parsed InterpreterString.
215
+ # The terminating element is pushed back onto the stream.
216
+ #
217
+ # If _io_ encounters EOF before the parsing is finished, an
218
+ # UnterminatedString exception is raised, unless the _eoerror_
219
+ # is false.
220
+ #
221
+ # This is the *central* function of the parsing of files.
222
+ def self.parse_until_unquoted(io, term, eoerror = true)
223
+ string = InterpreterString.new
224
+ string.contents = LexicalAnalyzer.new(io, term).parse(eoerror)
225
+ return string
226
+ end
227
+
228
+ def initialize(contents = [])
229
+ @contents = contents
230
+ end
231
+
232
+ # Fully expand the InterpreterString to obtain a String object.
233
+ # _interpreter_ is the Interpreter object in which the expansion
234
+ # takes place.
235
+ def expand_to_string(interpreter)
236
+ pre_expanded = expand_all_variables(interpreter)
237
+ retval = ""
238
+ for type, value in pre_expanded.contents
239
+ retval += value
240
+ end
241
+ return retval
242
+ end
243
+
244
+ # Splits the string, after expansion, in the *unquoted* parts,
245
+ # where _re_ matches, and returns the corresponding array of
246
+ # strings.
247
+ #
248
+ # An empty expanded string expands to a null array.
249
+ def expand_and_split(re, interpreter)
250
+ pre_expanded = expand_all_variables(interpreter)
251
+ retval = []
252
+ cur_str = ""
253
+ for type, value in pre_expanded.contents
254
+ case type
255
+ when :quoted
256
+ cur_str += value
257
+ when :unquoted
258
+ tmp = value.split(re, -1)
259
+ cur_str += tmp[0]
260
+ # Push splitted stuff here:
261
+ while tmp.size > 1
262
+ retval << cur_str
263
+ tmp.shift
264
+ cur_str = tmp[0]
265
+ end
266
+ end
267
+ end
268
+ retval << cur_str
269
+ if (retval.size == 1) && (retval.first == "")
270
+ return []
271
+ end
272
+ return retval
273
+ end
274
+
275
+ protected
276
+
277
+ # Returns a new InterpreterString object with all variables
278
+ # expanded. _interpreter_ is the Interpreter in which the
279
+ # expansion takes place.
280
+ def expand_all_variables(interpreter)
281
+ c = []
282
+ for type, value in @contents
283
+ case type
284
+ when :quoted_variable
285
+ c << [:quoted, interpreter.variables.
286
+ expand_variable(value, interpreter)]
287
+ when :unquoted_variable
288
+ c << [:unquoted, interpreter.variables.
289
+ expand_variable(value, interpreter)]
290
+ else
291
+ c << [type, value]
292
+ end
293
+ end
294
+ return InterpreterString.new(c)
295
+ end
296
+
297
+
298
+ end
299
+
300
+ end
301
+
302
+ end
303
+