Class: Layo::Parser
- Inherits:
-
Object
- Object
- Layo::Parser
- Defined in:
- lib/layo/parser.rb
Instance Attribute Summary collapse
-
#functions ⇒ Object
readonly
Returns the value of attribute functions.
-
#tokenizer ⇒ Object
Returns the value of attribute tokenizer.
Instance Method Summary collapse
- #expect_token(*types) ⇒ Object
-
#initialize(tokenizer) ⇒ Parser
constructor
A new instance of Parser.
-
#next_expression ⇒ Object
Returns internal name of the next expression.
- #next_statement ⇒ Object
- #parse_assignment_statement ⇒ Object
- #parse_binary_expression ⇒ Object
- #parse_block ⇒ Object
- #parse_break_statement ⇒ Object
- #parse_cast_expression ⇒ Object
- #parse_cast_statement ⇒ Object
- #parse_condition_statement ⇒ Object
- #parse_constant_expression ⇒ Object
- #parse_declaration_statement ⇒ Object
- #parse_expression(name = nil) ⇒ Object
- #parse_expression_statement ⇒ Object
-
#parse_function_declarations ⇒ Object
Function declarations should be parsed first in order to properly parse argument list and allow calling functions before their definition.
- #parse_function_statement ⇒ Object
-
#parse_identifier_expression ⇒ Object
Identifier expression represents two types of expressions: variable expression: returns value of variable function call expression: returns value of function call.
- #parse_input_statement ⇒ Object
- #parse_loop_statement ⇒ Object
- #parse_nary_expression ⇒ Object
- #parse_print_statement ⇒ Object
- #parse_program ⇒ Object (also: #parse)
- #parse_return_statement ⇒ Object
- #parse_statement(name = nil) ⇒ Object
- #parse_switch_statement ⇒ Object
- #parse_unary_expression ⇒ Object
- #skip_newlines ⇒ Object
Constructor Details
#initialize(tokenizer) ⇒ Parser
Returns a new instance of Parser.
6 7 8 |
# File 'lib/layo/parser.rb', line 6 def initialize(tokenizer) @tokenizer, @functions = tokenizer, {} end |
Instance Attribute Details
#functions ⇒ Object (readonly)
Returns the value of attribute functions.
4 5 6 |
# File 'lib/layo/parser.rb', line 4 def functions @functions end |
#tokenizer ⇒ Object
Returns the value of attribute tokenizer.
3 4 5 |
# File 'lib/layo/parser.rb', line 3 def tokenizer @tokenizer end |
Instance Method Details
#expect_token(*types) ⇒ Object
63 64 65 66 67 |
# File 'lib/layo/parser.rb', line 63 def expect_token(*types) token = @tokenizer.next raise UnexpectedTokenError, token unless types.include?(token[:type]) token end |
#next_expression ⇒ Object
Returns internal name of the next expression
281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/layo/parser.rb', line 281 def next_expression return 'binary' if @tokenizer.try([ :sum_of, :diff_of, :produkt_of, :quoshunt_of, :mod_of, :biggr_of, :smallr_of, :both_of, :either_of, :won_of, :both_saem, :diffrint ]) return 'cast' if @tokenizer.try(:maek) return 'constant' if @tokenizer.try([:boolean, :integer, :float, :string]) return 'identifier' if @tokenizer.try(:identifier) return 'nary' if @tokenizer.try([:all_of, :any_of, :smoosh]) return 'unary' if @tokenizer.try(:not) nil end |
#next_statement ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/layo/parser.rb', line 87 def next_statement return 'assignment' if @tokenizer.try(:identifier, :r) return 'break' if @tokenizer.try(:gtfo) return 'cast' if @tokenizer.try(:identifier, :is_now_a) return 'condition' if @tokenizer.try(:o_rly?) return 'declaration' if @tokenizer.try(:i_has_a) return 'function' if @tokenizer.try(:how_duz_i) return 'input' if @tokenizer.try(:gimmeh) return 'loop' if @tokenizer.try(:im_in_yr) return 'print' if @tokenizer.try(:visible) return 'return' if @tokenizer.try(:found_yr) return 'switch' if @tokenizer.try(:wtf?) return 'expression' if !next_expression.nil? nil end |
#parse_assignment_statement ⇒ Object
116 117 118 119 120 121 |
# File 'lib/layo/parser.rb', line 116 def parse_assignment_statement attrs = { identifier: expect_token(:identifier)[:data] } expect_token(:r) attrs[:expression] = parse_expression Ast::Statement.new('assignment', attrs) end |
#parse_binary_expression ⇒ Object
304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/layo/parser.rb', line 304 def parse_binary_expression attrs = { operator: expect_token( :sum_of, :diff_of, :produkt_of, :quoshunt_of, :mod_of, :biggr_of, :smallr_of, :both_of, :either_of, :won_of, :both_saem, :diffrint )[:type] } attrs[:left] = parse_expression @tokenizer.next if @tokenizer.peek[:type] == :an @tokenizer.unpeek attrs[:right] = parse_expression Ast::Expression.new('binary', attrs) end |
#parse_block ⇒ Object
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/layo/parser.rb', line 76 def parse_block statements = [] begin skip_newlines unless (name = next_statement).nil? statements << parse_statement(name) end end until name.nil? Ast::Block.new(statements) end |
#parse_break_statement ⇒ Object
123 124 125 126 |
# File 'lib/layo/parser.rb', line 123 def parse_break_statement expect_token(:gtfo) Ast::Statement.new('break') end |
#parse_cast_expression ⇒ Object
318 319 320 321 322 323 324 |
# File 'lib/layo/parser.rb', line 318 def parse_cast_expression expect_token(:maek) attrs = { being_casted: parse_expression } expect_token(:a) attrs[:to] = expect_token(:noob, :troof, :numbr, :numbar, :yarn)[:type] Ast::Expression.new('cast', attrs) end |
#parse_cast_statement ⇒ Object
128 129 130 131 132 133 |
# File 'lib/layo/parser.rb', line 128 def parse_cast_statement attrs = { identifier: expect_token(:identifier)[:data] } expect_token(:is_now_a) attrs[:to] = expect_token(:noob, :troof, :numbr, :numbar, :yarn)[:type] Ast::Statement.new('cast', attrs) end |
#parse_condition_statement ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/layo/parser.rb', line 135 def parse_condition_statement expect_token(:o_rly?) expect_token(:newline) expect_token(:ya_rly) expect_token(:newline) attrs = { then: parse_block, elseif: [] } while @tokenizer.peek[:type] == :mebbe expect_token(:mebbe) condition = parse_expression expect_token(:newline) attrs[:elseif] << { condition: condition, block: parse_block } end @tokenizer.unpeek if @tokenizer.peek[:type] == :no_wai expect_token(:no_wai) expect_token(:newline) attrs[:else] = parse_block end @tokenizer.unpeek expect_token(:oic) Ast::Statement.new('condition', attrs) end |
#parse_constant_expression ⇒ Object
326 327 328 329 |
# File 'lib/layo/parser.rb', line 326 def parse_constant_expression token = expect_token(:boolean, :integer, :float, :string) Ast::Expression.new('constant', { vtype: token[:type], value: token[:data] }) end |
#parse_declaration_statement ⇒ Object
158 159 160 161 162 163 164 165 166 167 |
# File 'lib/layo/parser.rb', line 158 def parse_declaration_statement expect_token(:i_has_a) attrs = { identifier: expect_token(:identifier)[:data] } if @tokenizer.peek[:type] == :itz @tokenizer.next attrs[:initialization] = parse_expression end @tokenizer.unpeek Ast::Statement.new('declaration', attrs) end |
#parse_expression(name = nil) ⇒ Object
294 295 296 297 298 299 300 301 302 |
# File 'lib/layo/parser.rb', line 294 def parse_expression(name = nil) token = @tokenizer.peek @tokenizer.unpeek name = next_expression unless name unless name raise SyntaxError.new(token[:line], token[:pos], 'Expected expression') end send("parse_#{name}_expression".to_sym) end |
#parse_expression_statement ⇒ Object
169 170 171 172 |
# File 'lib/layo/parser.rb', line 169 def parse_expression_statement attrs = { expression: parse_expression } Ast::Statement.new('expression', attrs) end |
#parse_function_declarations ⇒ Object
Function declarations should be parsed first in order to properly parse argument list and allow calling functions before their definition. So this method should be called as the first pass before parsing begins
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 |
# File 'lib/layo/parser.rb', line 13 def parse_function_declarations @functions = {} @tokenizer.reset_peek until (token = @tokenizer.peek)[:type] == :eof if token[:type] == :how_duz_i # Function name must follow token = @tokenizer.peek unless token[:type] == :identifier raise UnexpectedTokenError, token end name = token[:data] args = [] token = @tokenizer.peek if token[:type] == :yr # Function arguments must follow begin token = @tokenizer.peek unless token[:type] == :identifier raise UnexpectedTokenError, token end args << token[:data] end while @tokenizer.peek[:type] == :an_yr end @tokenizer.unpeek @functions[name] = args # Newline must follow token = @tokenizer.peek unless token[:type] == :newline raise UnexpectedTokenError, token end end end @tokenizer.reset_peek end |
#parse_function_statement ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/layo/parser.rb', line 174 def parse_function_statement expect_token(:how_duz_i) name = expect_token(:identifier)[:data] if @functions.has_key?(name) # Function definition was parsed in the first pass until @tokenizer.next[:type] == :newline; end args = @functions[name] else # Parse argument list as usual args = [] if @tokenizer.peek[:type] == :yr begin @tokenizer.next args << expect_token(:identifier)[:data] end while @tokenizer.peek[:type] == :an_yr end @tokenizer.unpeek expect_token(:newline) @functions[name] = args end block = parse_block expect_token(:if_u_say_so) Ast::Statement.new('function', { name: name, args: args, block: block }) end |
#parse_identifier_expression ⇒ Object
Identifier expression represents two types of expressions:
variable expression: returns value of variable
function call expression: returns value of function call
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/layo/parser.rb', line 334 def parse_identifier_expression name = expect_token(:identifier)[:data] begin function = self.functions.fetch(name) # Function call attrs = { name: name, parameters: [] } function.size.times do |c| attrs[:parameters] << parse_expression end return Ast::Expression.new('function', attrs) rescue KeyError # Variable name return Ast::Expression.new('variable', name: name) end end |
#parse_input_statement ⇒ Object
199 200 201 202 203 |
# File 'lib/layo/parser.rb', line 199 def parse_input_statement expect_token(:gimmeh) attrs = { identifier: expect_token(:identifier)[:data] } Ast::Statement.new('input', attrs) end |
#parse_loop_statement ⇒ Object
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/layo/parser.rb', line 205 def parse_loop_statement loop_start = expect_token(:im_in_yr) label_begin = expect_token(:identifier)[:data] attrs = {} if [:uppin, :nerfin, :identifier].include?(@tokenizer.peek[:type]) attrs[:op] = expect_token(:uppin, :nerfin, :identifier) expect_token(:yr) attrs[:op] = attrs[:op][:type] == :identifier ? attrs[:op][:data] : attrs[:op][:type] attrs[:counter] = expect_token(:identifier)[:data] end @tokenizer.unpeek if [:til, :wile].include?(@tokenizer.peek[:type]) attrs[:guard] = { type: expect_token(:til, :wile)[:type] } attrs[:guard][:expression] = parse_expression end @tokenizer.unpeek attrs[:block] = parse_block expect_token(:im_outta_yr) label_end = expect_token(:identifier)[:data] unless label_begin == label_end raise SyntaxError.new( loop_start[:line], loop_start[:pos], "Loop labels don't match: '#{label_begin}' and '#{label_end}'" ) end attrs[:label] = label_begin Ast::Statement.new('loop', attrs) end |
#parse_nary_expression ⇒ Object
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/layo/parser.rb', line 350 def parse_nary_expression attrs = { operator: expect_token(:all_of, :any_of, :smoosh)[:type] } attrs[:expressions] = [parse_expression] while true @tokenizer.next if @tokenizer.peek[:type] == :an @tokenizer.unpeek name = next_expression if name.nil? then break else attrs[:expressions] << parse_expression(name) end end # We need either MKAY or Newline here, but # should consume only MKAY if present token = @tokenizer.peek unless [:mkay, :newline].include?(token[:type]) raise UnexpectedTokenError, token end @tokenizer.next if token[:type] == :mkay @tokenizer.unpeek Ast::Expression.new('nary', attrs) end |
#parse_print_statement ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/layo/parser.rb', line 235 def parse_print_statement expect_token(:visible) attrs = { expressions: [parse_expression] } until (name = next_expression).nil? attrs[:expressions] << parse_expression(name) end attrs[:suppress] = false if @tokenizer.peek[:type] == :exclamation @tokenizer.next attrs[:suppress] = true end @tokenizer.unpeek Ast::Statement.new('print', attrs) end |
#parse_program ⇒ Object Also known as: parse
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/layo/parser.rb', line 48 def parse_program parse_function_declarations skip_newlines expect_token(:hai) version = expect_token(:float)[:data] expect_token(:newline) block = parse_block expect_token(:kthxbye) skip_newlines expect_token(:eof) Ast::Program.new(version, block) end |
#parse_return_statement ⇒ Object
250 251 252 253 254 |
# File 'lib/layo/parser.rb', line 250 def parse_return_statement expect_token(:found_yr) attrs = { expression: parse_expression } Ast::Statement.new('return', attrs) end |
#parse_statement(name = nil) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/layo/parser.rb', line 103 def parse_statement(name = nil) token = @tokenizer.peek @tokenizer.unpeek name = next_statement unless name unless name raise SyntaxError.new(token[:line], token[:pos], 'Expected statement') end statement = send("parse_#{name}_statement".to_sym) expect_token(:newline) statement.line = token[:line] statement end |
#parse_switch_statement ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/layo/parser.rb', line 256 def parse_switch_statement expect_token(:wtf?) expect_token(:newline) parse_case = lambda do expect_token(:omg) expression = parse_expression('constant') expect_token(:newline) { expression: expression, block: parse_block } end attrs = { cases: [parse_case.call] } while @tokenizer.peek[:type] == :omg attrs[:cases] << parse_case.call end @tokenizer.unpeek if @tokenizer.peek[:type] == :omgwtf expect_token(:omgwtf) expect_token(:newline) attrs[:default] = parse_block end @tokenizer.unpeek expect_token(:oic) Ast::Statement.new('switch', attrs) end |
#parse_unary_expression ⇒ Object
370 371 372 373 |
# File 'lib/layo/parser.rb', line 370 def parse_unary_expression expect_token(:not) Ast::Expression.new('unary', { expression: parse_expression } ) end |
#skip_newlines ⇒ Object
69 70 71 72 73 74 |
# File 'lib/layo/parser.rb', line 69 def skip_newlines while @tokenizer.peek[:type] == :newline @tokenizer.next end @tokenizer.unpeek end |