Class: Voodoo::Parser
- Inherits:
-
Object
- Object
- Voodoo::Parser
- Defined in:
- lib/voodoo/parser.rb
Overview
Voodoo parser. The parser reads Voodoo source code and turns it into Ruby objects.
The public interface to Parser consists of the methods #new and #parse_top_level
Example usage:
require 'voodoo/parser'
File.open('example.voo') do |infile|
parser = Voodoo::Parser.new infile
while (element = parser.parse_top_level)
puts element.inspect
end
end
Defined Under Namespace
Classes: Error, MultipleErrors, ParseError, ParserInternalError
Constant Summary collapse
- NUMBER_STARTER =
/\d|-/
- STRING_STARTER =
'"'
- SYMBOL_STARTER =
/[[:alpha:]]|\\/
Instance Method Summary collapse
-
#initialize(input) ⇒ Parser
constructor
Creates a parser using the specified object as input.
-
#parse_body(kind) ⇒ Object
Parses statements up to “end X”.
-
#parse_escape ⇒ Object
Parses an escape sequence.
-
#parse_number ⇒ Object
Parses a number.
-
#parse_string ⇒ Object
Parses a string.
-
#parse_symbol ⇒ Object
Parses a symbol.
-
#parse_top_level ⇒ Object
Parses a top-level element.
Constructor Details
#initialize(input) ⇒ Parser
Creates a parser using the specified object as input. The input object must support a method getc
, which must return the next character of the input, or nil
to indicate the end of the input has been reached.
31 32 33 34 35 36 37 38 |
# File 'lib/voodoo/parser.rb', line 31 def initialize input @input = input @input_name = input.respond_to?(:path) ? input.path : nil @start_line = @line = 1 @start_column = @column = 0 @lookahead = nil @text = '' end |
Instance Method Details
#parse_body(kind) ⇒ Object
Parses statements up to “end X”. kind should indicate the type of body being parsed: :block, :conditional, :function, or :group. Returns an array of statements.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 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 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/voodoo/parser.rb', line 167 def parse_body kind wrap_exceptions do body = [] errors = [] case kind when :function kind_text = 'function definition' else kind_text = kind.to_s end # Groups are allowed to contain top-level statements. # All other kinds aren't. top_level = kind == :group done = false until done begin with_position do statement = parse_top_level_nonvalidating if statement == nil done = true parse_error "End of input while inside #{kind_text}", nil elsif statement[0] == :end # Done parsing body done = true elsif kind == :conditional && statement[0] == :else # Done parsing body, but there is another one coming up body << statement done = true else # Should be a normal statement. Validate it, then add it to body begin if top_level Validator.validate_top_level statement else Validator.validate_statement statement end body << statement rescue Validator::ValidationError => e magic_word = statement[0] if !top_level && Validator::TOP_LEVELS.member?(magic_word) && !Validator::STATEMENTS.member?(magic_word) parse_error "#{magic_word} is only allowed at top-level" else parse_error e. end end end end rescue => e # Got some kind of error. Still try to parse the rest of the body. errors << e end end # Raise error if we had just one. # If we had more than one, raise a MultipleErrors instance. if errors.length == 1 raise errors[0] elsif errors.length > 1 raise MultipleErrors.new errors end body end end |
#parse_escape ⇒ Object
Parses an escape sequence. This method should be called while the lookahead is the escape character (backslash). It decodes the escape sequence and returns the result as a string.
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/voodoo/parser.rb', line 239 def parse_escape wrap_exceptions do result = nil consume case lookahead when :eof parse_error "Unexpected end of input in escape sequence", nil when "\\", "\"", " " result = lookahead consume when "n" # \n is newline consume result = "\n" when "r" # \r is carriage return consume result = "\r" when "x" # \xXX is byte with hex value XX code = @input.read 2 @column = @column + 2 consume @text << code result = [code].pack('H2') when "\n" # \<newline> is line continuation character consume # Skip indentation of next line while lookahead =~ /\s/ consume end result = '' else # Default to just passing on next character result = lookahead consume end result end end |
#parse_number ⇒ Object
Parses a number. This method should be called while the lookahead is the first character of the number.
284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/voodoo/parser.rb', line 284 def parse_number wrap_exceptions do text = lookahead consume while lookahead =~ /\d/ text << lookahead consume end text.to_i end end |
#parse_string ⇒ Object
Parses a string. This method should be called while the lookahead is the opening double quote.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/voodoo/parser.rb', line 299 def parse_string wrap_exceptions do consume result = '' while true case lookahead when "\"" consume break when "\\" result << parse_escape else result << lookahead consume end end result end end |
#parse_symbol ⇒ Object
Parses a symbol. This method should be called while the lookahead is the first character of the symbol name.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/voodoo/parser.rb', line 322 def parse_symbol wrap_exceptions do name = '' while lookahead != :eof case lookahead when "\\" name << parse_escape when /\w|-/ name << lookahead consume when ':' # Colon parsed as last character of the symbol name name << lookahead consume break else break end end name.to_sym end end |
#parse_top_level ⇒ Object
Parses a top-level element. Returns an array containing the parts of the element.
Some examples (Voodoo code, Ruby return values in comments):
section functions
# [:section, :functions]
call foo x 12
# [:call, :foo, :x, 12]
set x add x 42
# [:set, :x, :add, :x, 42]
set-byte @x 1 10
# [:"set-byte", [:"@", :x], 1, 10]
ifeq x y
set z equal
else
set z not-equal
end if
# [:ifeq, [:x, :y], [[:set, :z, :equal]], [[:set, :z, :"not-equal"]]]
foo:
# [:label, :foo]
function x y
let z add x y
return z
end function
# [:function, [:x, :y], [:let, :z, :add, :x, :y], [:return, :z]]
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/voodoo/parser.rb', line 152 def parse_top_level wrap_exceptions do @text = '' # Skip whitespace, comments, and empty lines skip_to_next_top_level validate_top_level do parse_top_level_nonvalidating end end end |