aboutsummaryrefslogtreecommitdiffstats
path: root/test/racc/assets/intp.y
diff options
context:
space:
mode:
Diffstat (limited to 'test/racc/assets/intp.y')
-rw-r--r--test/racc/assets/intp.y546
1 files changed, 546 insertions, 0 deletions
diff --git a/test/racc/assets/intp.y b/test/racc/assets/intp.y
new file mode 100644
index 0000000000..24e547da61
--- /dev/null
+++ b/test/racc/assets/intp.y
@@ -0,0 +1,546 @@
+#
+# intp
+#
+
+class Intp::Parser
+
+prechigh
+ nonassoc UMINUS
+ left '*' '/'
+ left '+' '-'
+ nonassoc EQ
+preclow
+
+rule
+
+ program : stmt_list
+ {
+ result = RootNode.new( val[0] )
+ }
+
+ stmt_list :
+ {
+ result = []
+ }
+ | stmt_list stmt EOL
+ {
+ result.push val[1]
+ }
+ | stmt_list EOL
+
+ stmt : expr
+ | assign
+ | IDENT realprim
+ {
+ result = FuncallNode.new( @fname, val[0][0],
+ val[0][1], [val[1]] )
+ }
+ | if_stmt
+ | while_stmt
+ | defun
+
+ if_stmt : IF stmt THEN EOL stmt_list else_stmt END
+ {
+ result = IfNode.new( @fname, val[0][0],
+ val[1], val[4], val[5] )
+ }
+
+ else_stmt : ELSE EOL stmt_list
+ {
+ result = val[2]
+ }
+ |
+ {
+ result = nil
+ }
+
+ while_stmt: WHILE stmt DO EOL stmt_list END
+ {
+ result = WhileNode.new(@fname, val[0][0],
+ val[1], val[4])
+ }
+
+ defun : DEF IDENT param EOL stmt_list END
+ {
+ result = DefNode.new(@fname, val[0][0], val[1][1],
+ Function.new(@fname, val[0][0], val[2], val[4]))
+ }
+
+ param : '(' name_list ')'
+ {
+ result = val[1]
+ }
+ | '(' ')'
+ {
+ result = []
+ }
+ |
+ {
+ result = []
+ }
+
+ name_list : IDENT
+ {
+ result = [ val[0][1] ]
+ }
+ | name_list ',' IDENT
+ {
+ result.push val[2][1]
+ }
+
+ assign : IDENT '=' expr
+ {
+ result = AssignNode.new(@fname, val[0][0], val[0][1], val[2])
+ }
+
+ expr : expr '+' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]])
+ }
+ | expr '-' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]])
+ }
+ | expr '*' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]])
+ }
+ | expr '/' expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno,
+ '/', [val[0], val[2]])
+ }
+ | expr EQ expr
+ {
+ result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]])
+ }
+ | primary
+
+ primary : realprim
+ | '(' expr ')'
+ {
+ result = val[1]
+ }
+ | '-' expr =UMINUS
+ {
+ result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]])
+ }
+
+ realprim : IDENT
+ {
+ result = VarRefNode.new(@fname, val[0][0],
+ val[0][1])
+ }
+ | NUMBER
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | STRING
+ {
+ result = StringNode.new(@fname, *val[0])
+ }
+ | TRUE
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | FALSE
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | NIL
+ {
+ result = LiteralNode.new(@fname, *val[0])
+ }
+ | funcall
+
+ funcall : IDENT '(' args ')'
+ {
+ result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2])
+ }
+ | IDENT '(' ')'
+ {
+ result = FuncallNode.new(@fname, val[0][0], val[0][1], [])
+ }
+
+ args : expr
+ {
+ result = val
+ }
+ | args ',' expr
+ {
+ result.push val[2]
+ }
+
+end
+
+---- header
+#
+# intp/parser.rb
+#
+
+---- inner
+
+ def initialize
+ @scope = {}
+ end
+
+ RESERVED = {
+ 'if' => :IF,
+ 'else' => :ELSE,
+ 'while' => :WHILE,
+ 'then' => :THEN,
+ 'do' => :DO,
+ 'def' => :DEF,
+ 'true' => :TRUE,
+ 'false' => :FALSE,
+ 'nil' => :NIL,
+ 'end' => :END
+ }
+
+ RESERVED_V = {
+ 'true' => true,
+ 'false' => false,
+ 'nil' => nil
+ }
+
+ def parse(f, fname)
+ @q = []
+ @fname = fname
+ lineno = 1
+ f.each do |line|
+ line.strip!
+ until line.empty?
+ case line
+ when /\A\s+/, /\A\#.*/
+ ;
+ when /\A[a-zA-Z_]\w*/
+ word = $&
+ @q.push [(RESERVED[word] || :IDENT),
+ [lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]]
+ when /\A\d+/
+ @q.push [:NUMBER, [lineno, $&.to_i]]
+ when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
+ @q.push [:STRING, [lineno, eval($&)]]
+ when /\A==/
+ @q.push [:EQ, [lineno, '==']]
+ when /\A./
+ @q.push [$&, [lineno, $&]]
+ else
+ raise RuntimeError, 'must not happen'
+ end
+ line = $'
+ end
+ @q.push [:EOL, [lineno, nil]]
+ lineno += 1
+ end
+ @q.push [false, '$']
+ do_parse
+ end
+
+ def next_token
+ @q.shift
+ end
+
+ def on_error(t, v, values)
+ if v
+ line = v[0]
+ v = v[1]
+ else
+ line = 'last'
+ end
+ raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}"
+ end
+
+---- footer
+# intp/node.rb
+
+module Intp
+
+ class IntpError < StandardError; end
+ class IntpArgumentError < IntpError; end
+
+ class Core
+
+ def initialize
+ @ftab = {}
+ @obj = Object.new
+ @stack = []
+ @stack.push Frame.new '(toplevel)'
+ end
+
+ def frame
+ @stack[-1]
+ end
+
+ def define_function(fname, node)
+ raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname)
+ @ftab[fname] = node
+ end
+
+ def call_function_or(fname, args)
+ call_intp_function_or(fname, args) {
+ call_ruby_toplevel_or(fname, args) {
+ yield
+ }
+ }
+ end
+
+ def call_intp_function_or(fname, args)
+ if func = @ftab[fname]
+ frame = Frame.new(fname)
+ @stack.push frame
+ func.call self, frame, args
+ @stack.pop
+ else
+ yield
+ end
+ end
+
+ def call_ruby_toplevel_or(fname, args)
+ if @obj.respond_to? fname, true
+ @obj.send fname, *args
+ else
+ yield
+ end
+ end
+
+ end
+
+ class Frame
+
+ def initialize(fname)
+ @fname = fname
+ @lvars = {}
+ end
+
+ attr :fname
+
+ def lvar?(name)
+ @lvars.key? name
+ end
+
+ def [](key)
+ @lvars[key]
+ end
+
+ def []=(key, val)
+ @lvars[key] = val
+ end
+
+ end
+
+
+ class Node
+
+ def initialize(fname, lineno)
+ @filename = fname
+ @lineno = lineno
+ end
+
+ attr_reader :filename
+ attr_reader :lineno
+
+ def exec_list(intp, nodes)
+ v = nil
+ nodes.each {|i| v = i.evaluate(intp) }
+ v
+ end
+
+ def intp_error!(msg)
+ raise IntpError, "in #{filename}:#{lineno}: #{msg}"
+ end
+
+ def inspect
+ "#{self.class.name}/#{lineno}"
+ end
+
+ end
+
+
+ class RootNode < Node
+
+ def initialize(tree)
+ super nil, nil
+ @tree = tree
+ end
+
+ def evaluate
+ exec_list Core.new, @tree
+ end
+
+ end
+
+
+ class DefNode < Node
+
+ def initialize(file, lineno, fname, func)
+ super file, lineno
+ @funcname = fname
+ @funcobj = func
+ end
+
+ def evaluate(intp)
+ intp.define_function @funcname, @funcobj
+ end
+
+ end
+
+ class FuncallNode < Node
+
+ def initialize(file, lineno, func, args)
+ super file, lineno
+ @funcname = func
+ @args = args
+ end
+
+ def evaluate(intp)
+ args = @args.map {|i| i.evaluate intp }
+ begin
+ intp.call_intp_function_or(@funcname, args) {
+ if args.empty? or not args[0].respond_to?(@funcname)
+ intp.call_ruby_toplevel_or(@funcname, args) {
+ intp_error! "undefined function #{@funcname.id2name}"
+ }
+ else
+ recv = args.shift
+ recv.send @funcname, *args
+ end
+ }
+ rescue IntpArgumentError, ArgumentError
+ intp_error! $!.message
+ end
+ end
+
+ end
+
+ class Function < Node
+
+ def initialize(file, lineno, params, body)
+ super file, lineno
+ @params = params
+ @body = body
+ end
+
+ def call(intp, frame, args)
+ unless args.size == @params.size
+ raise IntpArgumentError,
+ "wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})"
+ end
+ args.each_with_index do |v,i|
+ frame[@params[i]] = v
+ end
+ exec_list intp, @body
+ end
+
+ end
+
+
+ class IfNode < Node
+
+ def initialize(fname, lineno, cond, tstmt, fstmt)
+ super fname, lineno
+ @condition = cond
+ @tstmt = tstmt
+ @fstmt = fstmt
+ end
+
+ def evaluate(intp)
+ if @condition.evaluate(intp)
+ exec_list intp, @tstmt
+ else
+ exec_list intp, @fstmt if @fstmt
+ end
+ end
+
+ end
+
+ class WhileNode < Node
+
+ def initialize(fname, lineno, cond, body)
+ super fname, lineno
+ @condition = cond
+ @body = body
+ end
+
+ def evaluate(intp)
+ while @condition.evaluate(intp)
+ exec_list intp, @body
+ end
+ end
+
+ end
+
+
+ class AssignNode < Node
+
+ def initialize(fname, lineno, vname, val)
+ super fname, lineno
+ @vname = vname
+ @val = val
+ end
+
+ def evaluate(intp)
+ intp.frame[@vname] = @val.evaluate(intp)
+ end
+
+ end
+
+ class VarRefNode < Node
+
+ def initialize(fname, lineno, vname)
+ super fname, lineno
+ @vname = vname
+ end
+
+ def evaluate(intp)
+ if intp.frame.lvar?(@vname)
+ intp.frame[@vname]
+ else
+ intp.call_function_or(@vname, []) {
+ intp_error! "unknown method or local variable #{@vname.id2name}"
+ }
+ end
+ end
+
+ end
+
+ class StringNode < Node
+
+ def initialize(fname, lineno, str)
+ super fname, lineno
+ @val = str
+ end
+
+ def evaluate(intp)
+ @val.dup
+ end
+
+ end
+
+ class LiteralNode < Node
+
+ def initialize(fname, lineno, val)
+ super fname, lineno
+ @val = val
+ end
+
+ def evaluate(intp)
+ @val
+ end
+
+ end
+
+end # module Intp
+
+begin
+ tree = nil
+ fname = 'src.intp'
+ File.open(fname) {|f|
+ tree = Intp::Parser.new.parse(f, fname)
+ }
+ tree.evaluate
+rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT
+ raise ####
+ $stderr.puts "#{File.basename $0}: #{$!}"
+ exit 1
+end