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
Instance Method Summary collapse
-
#initialize(input) ⇒ Parser
constructor
Creates a parser using the specified object as input.
-
#parse_body(kind) ⇒ Object
Parses a body for a function or a conditional.
-
#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.
27 28 29 30 31 32 33 34 |
# File 'lib/voodoo/parser.rb', line 27 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 a body for a function or a conditional
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 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 |
# File 'lib/voodoo/parser.rb', line 161 def parse_body kind wrap_exceptions do body = [] errors = [] case kind when :function kind_text = 'function definition' else kind_text = kind.to_s end 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 if statement[0] == :function parse_error "Function definitions are only allowed at top-level" end begin Validator.validate_statement statement body << statement rescue Validator::ValidationError => e parse_error e. 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.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/voodoo/parser.rb', line 222 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.
267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/voodoo/parser.rb', line 267 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.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/voodoo/parser.rb', line 282 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.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/voodoo/parser.rb', line 305 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]]
148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/voodoo/parser.rb', line 148 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 |