rubyで自作言語を作成する課題が出ました。
ある程度形になってきたのですがRubyでいう所のputsとifを同時に扱うと木構造にする段階でおかしくなってしまいます。
どうすればいいのでしょうか?
具体的には
elsif token == :put#putsを木構造に
result = expression
p ['F', [:put, result]]
return [:put, result]
elsif token == :if#ifを木構造に
result = expression
result2 = expression
return [:if, result,result2]
if 1==1 put aと入力した際、このあたりで
[:if ,[:eq,1,1], [:put, a]]のような形になって欲しいのに[:if ,[:eq,1,1], a]になってます。
Ruby
1#!/usr/bin/ruby 2require 'strscan' 3 4class Calc 5 DEBUG = true 6 7 @@keywords = { 8 '+' => :add, 9 '-' => :sub, 10 '*' => :mul, 11 '/' => :div, 12 '%' => :mod, 13 '(' => :lpar, 14 ')' => :rpar, 15 '==' => :eq, 16 '>=' => :bigger, 17 '<=' => :smaller, 18 '=' => :in, 19 'put' => :put, 20 'if' => :if 21 } 22 23 # 式 := 項 (('+'|'-') 項)* 24 # 項 := 因子 (('*'|'/') 因子)* 25 # 因子 := '-'? (リテラル | '(' 式 ')') 26 27 def get_token() 28 if ret = @scanner.scan(/\A\s*(#{@@keywords.keys.map{|t|Regexp.escape(t)}.join('|')})/)#キーワードのスキャン 29 return @@keywords[ret] 30 end 31 if ret = @scanner.scan(/\A\s*([0-9.]+)/)#数値のスキャン 32 return ret.to_f 33 end 34 if ret = @scanner.scan(/\A\s*\z/)#空白のスキャン 35 return nil 36 end 37 if ret = @scanner.scan(/\A\s*([A-Za-z_][A-Za-z_0-9]*)/) 38 return ret 39 end 40 return :bad_token 41 end 42 43 def unget_token() 44 @scanner.unscan 45 end 46 47 def expression() 48 result = term 49 while true 50 token = get_token 51 unless token == :add or token == :sub or token == :eq or token == :bigger or token == :smaller or token == :in 52 unget_token 53 break 54 end 55 result = [token, result, term] 56 end 57 p ['E', result] if Calc::DEBUG 58 return result 59 end 60 61 def term() 62 result = factor 63 while true 64 token = get_token 65 unless token == :mul or token == :div or token == :put or token == :if 66 unget_token 67 break 68 end 69 result = [token, result, factor] 70 end 71 p ['T', result] if Calc::DEBUG 72 return result 73 end 74 75 def factor() 76 token = get_token 77 minusflg = 1 78 if token == :sub 79 minusflg = -1 80 token = get_token 81 end 82 if token.is_a? Numeric 83 p ['F', token * minusflg] if Calc::DEBUG 84 return token * minusflg 85 elsif token == :lpar 86 result = expression 87 unless get_token == :rpar 88 raise Exception, "unexpected token" 89 end 90 p ['F', [:mul, minusflg, result]] if Calc::DEBUG 91 return [:mul, minusflg, result] 92 raise Exception, "unexpected token" 93 elsif token == :put#putsを木構造に 94 result = expression 95 return [:put, result] 96 elsif token == :if#ifを木構造に 97 result = expression 98 result2 = expression 99 return [:if, result,result2] 100 elsif token.is_a? String 101 p ['F', token] if Calc::DEBUG 102 return token 103 end 104 end 105 106 def eval(exp) 107 if exp.instance_of?(Array) 108 case exp[0] 109 when :add 110 return eval(exp[1]) + eval(exp[2]) 111 when :sub 112 return eval(exp[1]) - eval(exp[2]) 113 when :mul 114 return eval(exp[1]) * eval(exp[2]) 115 when :div 116 return eval(exp[1]) / eval(exp[2]) 117 when :if 118 if eval(exp[2]) 119 return eval(exp[3]) 120 else 121 return eval(exp[4]) 122 end 123 when :eq 124 if eval(exp[1]) == eval(exp[2]) then 125 return true 126 else 127 return false 128 end 129 when :bigger 130 if eval(exp[1]) >= eval(exp[2]) then 131 return true 132 else 133 return false 134 end 135 when :smaller 136 if eval(exp[1]) <= eval(exp[2]) then 137 return true 138 else 139 return false 140 end 141 when :put 142 return puts eval(exp[1]) 143 when :in 144 return eval("@#{eval(exp[1])} = #{eval(exp[2])}") 145 end 146 else 147 return exp 148 end 149 end 150 151 def initialize 152 loop do 153 print 'exp> ' 154 code = STDIN.gets # read 155 if ["quit\n", "q\n", "bye\n", "exit\n"].include?(code) then exit end 156 @scanner = StringScanner.new(code) 157 begin 158 ex = expression # eval 159 puts eval(ex) # print 160 rescue Exception 161 puts 'Bad Expression' 162 end 163 end 164 end 165end 166 167Calc.new 168
回答1件
あなたの回答
tips
プレビュー