git @ Cat's Eye Technologies Velo / e939d3c
Parser has been translated. Cat's Eye Technologies 11 years ago
1 changed file(s) with 157 addition(s) and 147 deletion(s). Raw diff Collapse all Expand all
22 --[[ ========== DEBUG ========= ]]--
33
44 local debug = function(s)
5 print("--> " .. s)
5 print("--> (" .. s .. ")")
66 end
77
88 --[[ ========== EXCEPTIONS ========= ]]--
3131
3232 Script = {}
3333 Script.new = function(exprs)
34 methods = {}
34 local methods = {}
3535
3636 methods.eval = function(obj, args)
3737 debug "eval #{self} on #{obj} with #{args}"
3838 local e = nil
3939 for i,expr in ipairs(exprs) do
40 e = expr:eval(obj, args)
40 e = expr.eval(obj, args)
4141 end
4242 return e
4343 end
5555
5656 Assignment = {}
5757 Assignment.new = function(object, field, expr)
58 methods = {}
58 local methods = {}
5959
6060 methods.eval = function(obj, args)
6161 debug "eval #{self} on #{obj} with #{args}"
62 local val = expr:eval(obj, args)
63 local receiver = object:eval(obj, args)
62 local val = expr.eval(obj, args)
63 local receiver = object.eval(obj, args)
6464 debug "setting #{@field} on #{receiver}"
65 receiver:set(field, val)
65 receiver.set(field, val)
6666 return val
6767 end
6868
8080
8181 Self = {}
8282 Self.new = function()
83 methods = {}
83 local methods = {}
8484
8585 methods.eval = function(obj, args)
8686 debug "eval #{self} on #{obj} with #{args}"
9696
9797 Lookup = {}
9898 Lookup.new = function(_receiver, _ident)
99 methods = {}
99 local methods = {}
100 methods.class = "Lookup"
101
102 debug(tostring(_receiver))
103 _receiver.foo = "hi"
100104
101105 methods.receiver = function()
102106 return _receiver
108112
109113 methods.eval = function(obj, args)
110114 debug "eval #{self} on #{obj} with #{args}"
111 local receiver = _receiver:eval(obj, args)
112 return receiver:lookup(_ident)
115 local receiver = _receiver.eval(obj, args)
116 return receiver.lookup(_ident)
113117 end
114118
115119 methods.to_s = function()
116120 return "Lookup(" .. _receiver.to_s() .. "," .. _ident .. ")"
117121 end
118
122
119123 return methods
120124 end
121125
122126 MethodCall = {}
123127 MethodCall.new = function(method_expr, exprs)
124 methods = {}
128 local methods = {}
125129
126130 methods.eval = function(obj, args)
127131 debug "eval #{self} on #{obj} with #{args}"
128132 local new_args = {}
129133 for i,expr in ipairs(exprs) do
130 new_args.push(expr:eval(obj, args))
131 end
132 local method = method_expr:eval(obj, args)
134 new_args.push(expr.eval(obj, args))
135 end
136 local method = method_expr.eval(obj, args)
133137 debug "arguments evaluated, now calling #{@method_expr} -> #{method}"
134 -- OOOH
135 if method.is_a_VeloMethod ~= nil then
138 if method.class == "VeloMethod" then
136139 --# xxx show receiver (method's bound object) in debug
137140 debug "running real method #{method} w/args #{args}"
138 return method:run(new_args)
141 return method.run(new_args)
139142 else
140143 debug "just returning non-method (#{method}) on call"
141144 return method
156159 Argument = {}
157160 Argument.new = function(num)
158161 num = num - 1
159 methods = {}
162 local methods = {}
160163
161164 methods.eval = function(obj, args)
162165 debug "eval #{self} on #{obj} with #{args}"
172175
173176 StringLiteral = {}
174177 StringLiteral.new = function(text)
175 methods = {}
178 local methods = {}
176179
177180 methods.eval = function(obj, args)
178181 debug "eval #{self} on #{obj} with #{args}"
189192 -- SANITY TEST
190193 local m = MethodCall.new(Self.new(), {Argument.new(1), StringLiteral.new("jonkers")})
191194 local a = Assignment.new(m, "bar", Lookup.new(Self.new(), "foo"))
192 s = Script.new({a, Self.new()}); print(s:to_s())
195 s = Script.new({a, Self.new()}); print(s.to_s())
193196
194197 function isdigit(s)
195198 return string.find("0123456789", s, 1, true) ~= nil
209212
210213 function isalnum(s)
211214 return isalpha(s) or isdigit(s)
215 end
216
217 function issep(s)
218 return string.find("(),.;=", s, 1, true) ~= nil
212219 end
213220
214221 --[[ ========== SCANNER ========= ]]--
219226 local _text = nil
220227 local _type = nil
221228
222 methods = {}
229 local methods = {}
223230
224231 methods.text = function() return _text end
225232 methods.type = function() return _type end
266273
267274 -- check for any single character tokens
268275 local c = string:sub(1,1)
269 local set = "(),.;="
270 if set:find(c, 1, true) ~= nil then
276 if issep(c) then
271277 string = string:sub(2)
272278 methods.set_token(c, "seperator")
273279 return
276282 -- check for arguments
277283 if string:sub(1,1) == "#" then
278284 local len = 0
279 while isdigit(string:sub(2+len,2+len)) do
285 while isdigit(string:sub(2+len,2+len)) and len <= string:len() do
280286 len = len + 1
281287 end
282288 if len > 0 then
290296 -- check for strings of "word" characters
291297 if isalnum(string:sub(1,1)) then
292298 local len = 0
293 while isalnum(string:sub(1+len,1+len)) do
299 while isalnum(string:sub(1+len,1+len)) and len <= string:len() do
294300 len = len + 1
295301 end
296302 local word = string:sub(1, 1+len-1)
326332
327333 methods.set_token('UNKNOWN', 'UNKNOWN')
328334 end
329
335
330336 methods.consume = function(s)
331337 if _text == s then
332338 methods.scan()
363369 end
364370 end
365371 if not good then
366 raise_VeloSyntaxError("expected '#{t}', found '#{@text}' (#{@type})")
372 local tstring = ""
373 for i,v in ipairs(types) do
374 tstring = tstring .. v .. ","
375 end
376 raise_VeloSyntaxError("expected '" .. tstring .. "', found '" ..
377 _text .. "' (" .. _type .. ")")
367378 end
368379 end
369380
374385 end
375386
376387 -- SANITY TEST
388 --[[
377389 x = Scanner.new(" \n (.#53) jonkers,031jon {sk}{str{ing}ity}w ")
378390 while not x.is_eof() do
379391 print(x.text() .. ":" .. x.type())
380392 x.scan()
381393 end
394 ]]--
382395
383396 --[[ ========== PARSER ========== ]]--
384397
405418 # | "(" [EOL] Expr ")"
406419 # .
407420
408 class Parser
409 def initialize s
410 @scanner = Scanner.new(s)
411 end
412
413 def script
414 debug "parsing Script production"
415 exprs = []
416 @scanner.consume_type "EOL"
417 e = expr
418 while not e.nil?
419 @scanner.expect_types ["EOL", "EOF"]
420 exprs.push(e)
421 @scanner.consume_type "EOL"
422 e = expr
423 end
424 Script.new(exprs)
425 end
426
427 def expr
428 debug "parsing Expr production"
429 if (['EOL', 'EOF'].include? @scanner.type or [')', ','].include? @scanner.text)
430 return nil
431 end
432 receiver = base # could be Expr, StringLit, Arg
433 if (['EOL', 'EOF'].include? @scanner.type or [')', ','].include? @scanner.text)
434 return MethodCall.new(receiver, [])
435 end
436 while @scanner.consume '.'
437 @scanner.consume_type 'EOL'
438 debug "parsing .ident"
439 ident = @scanner.text
440 @scanner.scan
441 receiver = Lookup.new(MethodCall.new(receiver, []), ident)
442 end
443 if @scanner.consume '='
444 # this is an assignment, so we must resolve the reciever chain
445 # as follows: a.b.c = foo becomes
446 # lookup(a, b).set(c, foo)
447 debug "unlookuping"
448 ident = nil
449 if receiver.is_a? Lookup
450 ident = receiver.ident
451 receiver = receiver.receiver
452 else
453 raise VeloSyntaxError, "assignment requires lvalue, but we have '#{@receiver}'"
454 end
455 debug "parsing assignment"
456 @scanner.consume_type 'EOL'
457 e = expr
458 return Assignment.new(receiver, ident, e)
459 elsif @scanner.type == 'EOF' or @scanner.type == 'EOL'
460 # this is a plain value, so we must resolve the reciever chain
461 # as follows: a.b.c becomes
462 # lookup(lookup(a, b), c)
463 debug "not a method call"
464 return MethodCall.new(receiver, [])
465 else
466 # this is a method call, so we must resolve the reciever chain
467 # as follows: a.b.c args becomes
468 # methodcall(lookup(lookup(a, b), c), args)
469 debug "parsing method call args"
470 args = []
471 e = expr
472 args.push(e) unless e.nil?
473 while @scanner.consume ","
474 @scanner.consume_type 'EOL'
475 e = expr
476 args.push(e) unless e.nil?
477 end
478 MethodCall.new(receiver, args)
479 end
480 end
481
482 def base
483 debug "parsing Base production"
484 if @scanner.consume "("
485 debug "parsing parens"
486 @scanner.consume_type 'EOL'
487 e = expr
488 @scanner.expect ")"
489 return e
490 elsif @scanner.type == 'strlit'
491 debug "parsing strlit"
492 s = @scanner.text
493 @scanner.scan
494 return StringLiteral.new(s)
495 elsif @scanner.type == 'arg'
496 debug "parsing arg"
497 num = @scanner.text.to_i
498 @scanner.scan
499 return Argument.new(num)
500 elsif @scanner.type == 'ident'
501 debug "parsing ident"
502 ident = @scanner.text
503 @scanner.scan
504 return Lookup.new(Self.new, ident)
505 else
506 raise VeloSyntaxError, "unexpected '#{@scanner.text}'"
507 end
508 end
509 end
510
511 if $0 == __FILE__
512 #$debug = true
513 p = Parser.new(ARGV[0])
514 s = p.script
515 puts s
516
517 if $debug
518 s1 = Parser.new('m a, m b, c').script
519 s2 = Parser.new('m a, (m b, c)').script
520 s3 = Parser.new('m a, (m b), c').script
521 puts s1
522 puts s2
523 puts s3
524 end
525 end
526421 ]]--
422
423 Parser = {}
424 Parser.new = function(s)
425 local scanner = Scanner.new(s)
426
427 local methods = {}
428
429 methods.script = function()
430 debug "parsing Script production"
431 local exprs = {}
432 scanner.consume_type "EOL"
433 local e = methods.expr()
434 while e ~= nil do
435 scanner.expect_types {"EOL", "EOF"}
436 exprs[#exprs+1] = e
437 scanner.consume_type "EOL"
438 e = methods.expr()
439 end
440 return Script.new(exprs)
441 end
442
443 methods.expr = function()
444 debug "parsing Expr production"
445 if (scanner.type() == "EOL" or scanner.type() == "EOF" or
446 scanner.text() == ")" or scanner.text() == ",") then
447 return nil
448 end
449 local receiver = methods.base() --# could be Expr, StringLit, Arg
450 if (scanner.type() == "EOL" or scanner.type() == "EOF" or
451 scanner.text() == ")" or scanner.text() == ",") then
452 return MethodCall.new(receiver, {})
453 end
454 while scanner.consume '.' do
455 scanner.consume_type 'EOL'
456 debug "parsing .ident"
457 ident = scanner.text()
458 scanner.scan()
459 receiver = Lookup.new(MethodCall.new(receiver, {}), ident)
460 end
461 if scanner.consume '=' then
462 -- this is an assignment, so we must resolve the reciever chain
463 -- as follows: a.b.c = foo becomes lookup(a, b).set(c, foo)
464 debug "unlookuping"
465 local ident = nil
466 if receiver.class == "Lookup" ~= nil then
467 ident = receiver.ident()
468 receiver = receiver.receiver()
469 else
470 raise_VeloSyntaxError("assignment requires lvalue, but we have '#{@receiver}'")
471 end
472 debug "parsing assignment"
473 scanner.consume_type 'EOL'
474 e = methods.expr()
475 return Assignment.new(receiver, ident, e)
476 elseif scanner.type() == 'EOF' or scanner.type() == 'EOL' then
477 -- this is a plain value, so we must resolve the reciever chain
478 -- as follows: a.b.c becomes lookup(lookup(a, b), c)
479 debug "not a method call"
480 return MethodCall.new(receiver, {})
481 else
482 -- this is a method call, so we must resolve the reciever chain
483 -- as follows: a.b.c args becomes
484 -- methodcall(lookup(lookup(a, b), c), args)
485 debug "parsing method call args"
486 local args = {}
487 local e = methods.expr()
488 if e ~= nil then
489 args[#args+1] = e
490 end
491 while scanner.consume "," do
492 scanner.consume_type 'EOL'
493 e = methods.expr()
494 if e ~= nil then
495 args[#args+1] = e
496 end
497 end
498 return MethodCall.new(receiver, args)
499 end
500 end
501
502 methods.base = function()
503 debug "parsing Base production"
504 if scanner.consume "(" then
505 debug "parsing parens"
506 scanner.consume_type "EOL"
507 e = methods.expr()
508 scanner.expect ")"
509 return e
510 elseif scanner.type() == "strlit" then
511 debug "parsing strlit"
512 s = scanner.text()
513 scanner.scan()
514 return StringLiteral.new(s)
515 elseif scanner.type() == "arg" then
516 debug "parsing arg"
517 num = scanner.text().to_i()
518 scanner.scan()
519 return Argument.new(num)
520 elseif scanner.type() == "ident" then
521 debug "parsing ident"
522 ident = scanner.text()
523 scanner.scan()
524 return Lookup.new(Self.new(), ident)
525 else
526 raise_VeloSyntaxError("unexpected '#{@scanner.text}'")
527 end
528 end
529
530 return methods
531 end
532
533 -- SANITY TEST
534 print(Parser.new('m a, m b, c').script().to_s())
535 print(Parser.new('m a, (m b, c)').script().to_s())
536 print(Parser.new('m a, (m b), c').script().to_s())
527537
528538 --[[ ========== RUNTIME ========= ]]--
529539