In Files

Parent

Voodoo::Parser

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

Public Class Methods

new(input) click to toggle source

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.

# File 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

Public Instance Methods

parse_body(kind) click to toggle source

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.

# File 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.message
              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() click to toggle source

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.

# File 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() click to toggle source

Parses a number. This method should be called while the lookahead is the first character of the number.

# File 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() click to toggle source

Parses a string. This method should be called while the lookahead is the opening double quote.

# File 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() click to toggle source

Parses a symbol. This method should be called while the lookahead is the first character of the symbol name.

# File voodoo/parser.rb, line 322
def parse_symbol
  parse_symbol1 ''
end
parse_symbol1(name) click to toggle source

Continues parsing a symbol. name the part of the symbol that has already been parsed.

# File voodoo/parser.rb, line 328
def parse_symbol1 name
  wrap_exceptions do
    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() click to toggle source

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]]
# File 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

[Validate]

Generated with the Darkfish Rdoc Generator 2.