init
[ikzolanguage.git] / test.y
blob4ce68d88d94cd38690707bb379dd3f1d547530d2
1 class TestParser
2 prechigh
3 nonassoc UMINUS
4 left '*' '/'
5 left '+' '-'
6 preclow
7 rule
8 program : stmt_list
10 result = RootNode.new(val[0])
13 stmt_list :
14 { result = [] }
15 | stmt_list stmt EOL
16 { result.push val[1] }
17 | stmt_list EOL
19 stmt : expr
20 | assign
21 | IDENT realprim
23 result = FuncallNode.new(@fname, val[0][0], val[0][1], [val[1]])
25 | if_stmt
26 | while_stmt
27 | defun
29 defun : DEF IDENT param EOL stmt_list END
31 result = DefNode.new(@fname, val[0][0], val[1][1],
32 Function.new(@fname, val[0][0], val[2], val[4]))
35 param : '(' name_list ')'
36 { result = val[1] }
37 | '(' ')'
38 { result = [] }
40 { result = [] }
42 name_list: IDENT
43 { result = [val[0][1]] }
44 | name_list ',' IDENT
45 { result.push val[2][1] }
47 if_stmt : IF stmt THEN EOL stmt_list else_stmt END
49 result = IfNode.new(@fname, val[0][0], val[1], val[4], val[5])
52 else_stmt: ELSE EOL stmt_list
54 result = val[2]
57 while_stmt: WHILE stmt DO EOL stmt_list END
59 result = WhileNode.new(@fname, val[0][0], val[1], val[4])
62 funcall : IDENT '(' args ')'
64 result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2])
66 | IDENT '(' ')'
68 result = FuncallNode.new(@fname, val[0][0], val[0][1], [])
71 args : expr
73 result = val
75 | args ',' expr
77 result.push val[2]
80 assign : IDENT '=' expr
82 result = AssignNode.new(@fname, val[0][0], val[0][1], val[2])
85 expr : expr '+' expr
87 result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]])
89 | expr '-' expr
91 result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]])
93 | expr '*' expr
95 result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]])
97 | expr '/' expr
99 result = FuncallNode.new(@fname, val[0].lineno, '/', [val[0], val[2]])
101 | primary
104 primary : realprim
105 | '(' expr ')'
106 { result = val[1] }
107 | '-' primary =UMINUS
108 { result = FuncallNode.new(@fname, val[0].lineno, '-@', [val[1]]) }
110 realprim: NIL
111 { result = NilNode.new(@fname, *val[0]) }
112 | TRUE
113 { result = TrueNode.new(@fname, *val[0]) }
114 | FALSE
115 { result = FalseNode.new(@fname, *val[0]) }
116 | IDENT
118 result = VarRefNode.new(@fname, val[0][0], val[0][1])
120 | NUMBER
122 result = LiteralNode.new(@fname, *val[0])
124 | STRING
126 result = StringNode.new(@fname, *val[0])
128 | funcall
132 ---- inner
134 require 'pp'
136 RESERVED = {
137 'if' => :IF,
138 'else' => :ELSE,
139 'while' => :WHILE,
140 'then' => :THEN,
141 'do' => :DO,
142 'end' => :END,
143 'nil' => :NIL,
144 'false' => :FALSE,
145 'true' => :TRUE
148 def parse(f, fname)
149 @q = []
150 @fname = fname
151 @lineno = 1
153 f.each do |line|
154 line.strip!
155 until line.empty?
156 case line
157 when /\A\s+/, /\A\#.*\z/
159 when /\A[a-zA-Z_]\w*/
160 word = $&
161 @q.push([RESERVED[word]||:IDENT, [@lineno, word.intern]])
162 when /\A\d+/
163 @q.push([:NUMBER, [@lineno, $&.to_i]])
164 when /\A['"](?:[^"'\\]+|\\.)*['"]/
165 @q.push([:STRING, [@lineno, eval($&)]])
166 when /\A./
167 @q.push([$&, [@lineno, $&]])
168 else
169 raise RuntimeError, 'must not happen'
171 line = $'
173 @q.push([:EOL, [@lineno, nil]])
175 @q.push([false, '$'])
176 @yydebug = true
177 do_parse
180 def next_token
181 @q.shift
184 def on_error(t, v, vstack)
185 if v
186 line = v[0]
187 v = v[1]
188 else
189 line = 'last'
191 raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}"
194 ---- footer
196 class IntpError < StandardError; end
198 class Node
199 def initialize(fname, linno)
200 @filename = fname
201 @lineno = lineno
204 attr :filename
205 attr :lineno
207 def exec_list(intp, nodes)
208 v = nil
209 nodes.each{|i| v = i.evaluate(intp) }
213 def intp_error!(msg)
214 raise IntpError, "in #{filename}:#{lineno}: #{msg}"
217 def inspect
218 "#{type.name}/#{lineno}"
222 class RootNode < Node
223 def initialize(tree)
224 super nil, nil
225 @tree = tree
228 def evaluate
229 exec_list Intp.new, @tree
233 class FuncallNode < Node
234 def initialize(fname, lineno, func, args)
235 super fname, lineno
236 @func = func
237 @args = args
240 def evaluate
241 arg = @args.collect{|i| i.evaluate}
242 recv = Object.new
243 if recv.respond_to?(@func, true)
245 elsif arg[0] && arg[0].respond_to?(@func)
246 recv = arg.shift
247 else
248 intp_error! "undefined method #{@func.id2name}"
250 recv.send(@func, *arg)
254 class IfNode < Node
255 def initialize(fname, lineno, cond, tstmt, fstmt)
256 super fname, lineno
257 @condition = cond
258 @tstmt = tstmt
259 @fstmt = fstmt
262 def evaluate
263 if @condition.evaluate
264 exec_list @tstmt
265 else
266 exec_list @fstmt if @fstmt
271 class WhileNode < Node
272 def initialize(fname, lineno, cond, body)
273 super fname, lineno
274 @condition = cond
275 @body = body
278 def evaluate
279 while @condition.evaluate != 0 do
280 exec_list @body
285 class AssignNode < Node
286 def initialize(fname, lineno, vtable, vname, val)
287 super fname, lineno
288 @vtable = vtable
289 @vname = vname
290 @val = val
293 def evaluate
294 @vtable[@vname] = @val.evaluate
298 class VarRefNode < Node
299 def initialize(fname, lineno, vname)
300 super fname, lineno
301 @vname = vname
304 def evaluate(intp)
305 if intp.frame.lvar? @vname
306 intp.frame[@vname]
307 else
308 intp.call_function_or(@vname, []) do
309 intp_error! "unknown method or local variable #{@vname.id2name}"
315 class StringNode < Node
316 def initialize(fname, lineno, str)
317 super fname, lineno
318 @val = str
321 def evaluate
322 @val.dup
326 class LiteralNode < Node
327 def initialize(fname, lineno, val)
328 super fname, lineno
329 @val = val
332 def evaluate
333 @val
337 class NilNode < Node
338 def initialize(fname, lineno, val)
339 super fname, lineno
342 def evaluate
347 class TrueNode < Node
348 def initialize(fname, lineno, val)
349 super fname, lineno
352 def evaluate
353 true
357 class FalseNode < Node
358 def initialize(fname, lineno, val)
359 super fname, lineno
362 def evaluate
363 false
367 class Intp
368 def Initialize
369 @ftab = {}
370 @obj = Object.new
371 @stack = []
372 @stack.push(IntpFrame.new('(Toplevel)'))
375 def frame
376 @stack.last
379 def define_function(fname, node)
380 if @ftab.key? fname
381 raise IntpError, "function #{fname.id2name} defined twice"
383 @ftab[fname] = node
386 def call_function_or(fname, args)
387 call_intp_function_or(fname, args) do
388 call_ruby_toplevel_or(fname, args) do
389 yield
394 def call_intp_function_or(fname, args)
395 if func = @ftab[fname]
396 frame = IntpFrame.new fname
397 @stack.push frame
398 func.call self, frame, args
399 @stack.pop
400 else
401 yield
405 def call_ruby_toplevel_or(fname, args)
406 if @obj.respond_to? fname, true
407 @obj.send fname, *args
408 else
409 yield
414 class IntpFrame
415 def initialize(fname)
416 @fname = fname
417 @lvars = {}
420 attr :fname
422 def lvar?(name)
423 @lvars.key? name
426 def [](key)
427 @lvars[key]
430 def []=(key, val)
431 @lvars[key] = val
435 class DefNode < Node
436 def initialize(file, nineno, fname, func)
437 super file, lineno
438 @funcname = fname
439 @funcobj = func
442 def evaluate(intp)
443 intp.define_function @funcname, @funcobj
447 class Function < Node
448 def initialize(file, lineno, param, body)
449 super file, lineno
450 @param = param
451 @body = body
454 def call(intp, frame, args)
455 unless args.size = @params.size
456 raise(IntpArgumentError, 'wrong # of arg for %S() (%d for %d)' % [frame.fname, args.size, @params.size])
458 args.each_with_index do |v,i|
459 frame[@params[i]] = v
461 exec_list intp, @body
465 class FuncallNode < Node
466 def initialize(file, lineno, func, args)
467 super file,lineno
468 @funcname = func
469 @args = args
472 def evaluate(intp)
473 arg = @args.collect{|i| i.evaluate intp }
475 begin
476 intp.call_function_or(@funcname, arg) do
477 if arg.empty? or !arg[0].respond_to? @funcname
478 intp_error! "undefined function #{@funcname.id2name}"
481 rescue INtpArgumentError, ArgumentError
482 intp_error! $!.message
487 begin
488 tree = nil
489 if ARGV[0]
490 File.open(ARGV[0]) do |f|
491 tree = TestParser.new.parse(f, ARGV[0])
493 else
494 tree = TestParser.new.parse($stdin, '-')
497 tree.evaluate
498 rescue Racc::ParseError, IntpError, Errno::ENOENT
499 $stderr.puts "#{File.basename($0)}: #{$!}"
500 exit 1