質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

1回答

1374閲覧

rubyで自作言語を作る

snowman

総合スコア25

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

1グッド

1クリップ

投稿2020/01/31 13:25

編集2020/02/01 07:17

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
DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2020/01/31 14:14

おかしくなる とは どの部分で、どういう条件の時に、どうなってほしいのに、どうなっちゃうのでしょう
snowman

2020/01/31 14:26

編集させていただきました!
winterboum

2020/01/31 20:54 編集

「条件」そうなる入力を載せていただかないと。 if の前からあると嬉しい その結果もつけて
snowman

2020/02/01 07:18

ifの前からとなると長くてどこまで書けばいいのかわからないのでコードのほうを参照してください 入力例については修正しました
winterboum

2020/02/01 09:55

多分 if の処理で余分に1トークン消費しているものと思われます。 読んでみます
guest

回答1

0

ベストアンサー

if の処理ではないですね。get_token のようです。
ret = @scanner.scan(/\A\s*(#{@@keywords.....
とあり、空白があるかもしれない後ろのkeywordを抜き出しているつもりのようですが、scanの場合、()の中を返すという仕組みではないので頭の空白付きになります。
ので、"put" ではなく " put" が ret に入り、return @@keywords[ret] がnilになってしまいます。

def get_token() @scanner.scan(/\A\s*/)

としてまず頭の空白を削除してみてください

追記
put が消えてしまうのは、空白の処理で解決しますが、思った通りの結果にならないのは expression の二項演算の処理がうまく行っていないようです。そこを見なおしてください

追記2
ではもう少し
0. expressionで:eq を見つけて result = [:eq, result, term] が実行されます
つまり [:eq, 1.0,term]
0. その term の入り口で result = factor にて ==の後ろの1が入り result=1.0
0. while loop に入って token に put が入り、result は
result = [:put,1.0,factor] となります
0. そのfactor は "a" を返すので result = [:put,1.0,"a"] が3の結果です
0. よって1の結果は [:eq,1, [:put,1.0,"a"]]

ということで、二項演算の二項目の処理を考えてください

投稿2020/02/01 11:49

編集2020/02/04 14:13
winterboum

総合スコア23284

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

snowman

2020/02/01 12:26

やってみましたがダメでした・・・ 一応、空白の場合nilが返ってくるようになっていて、unlessのところで消されるようになっています
winterboum

2020/02/01 12:58 編集

どうなったかを書いていただかないと、何が悪いのかわかりません。 あとその空白のところは、 1) keywordチェックより後ろ 2)最後に \z があるため文末でないとhitしない なので役に立たないです
snowman

2020/02/01 13:05

すみません、なにもかわらなかったです・・・
snowman

2020/02/01 13:09

ご指摘ありがとうございます・・・
winterboum

2020/02/01 21:38 編集

[:if, [:eq, 1.0, [:put, 1.0, "a"]], nil] こうなりました? if の1回目の expressionが思い通りでないようですね。
snowman

2020/02/03 07:51

[:if ,[:eq,1,1], [:put, a]]のままでした。
winterboum

2020/02/03 08:12

ということは 成功?
snowman

2020/02/04 10:14

すみません、コピペするところ間違えてました・・・
snowman

2020/02/04 10:15

[:if ,[:eq,1,1], a]です
winterboum

2020/02/04 10:18

def get_token() の直下に @scanner.scan(/\A\s*/) を入れても、ですか? @scanner.scan(/\A\s*\z/) でなく。
snowman

2020/02/04 12:01

すみません、 [:if, [:eq, 1.0, [:put, 1.0, "a"]], nil]]になりました! ありがとうございます しかし、まだ解決までは・・・
winterboum

2020/02/04 12:08

put が消えるのは解決しましたね? eq の中身が期待と異なるのは回答の追記に書いた通りです。
snowman

2020/02/04 12:21

すみません、そもそも最初の質問の内容が[:if ,[:eq,1,1], [:put, a]]としたいという内容なのですが 解決はしていませんがありがとうございます
snowman

2020/02/05 00:04

追記ありがとうございます! 頑張ってみます!
winterboum

2020/02/05 01:24

expression で if put 代入 なども処理してる所に難しさがあるかもしれません。 expression は式の処理に専念させる (if や put などを 式と見るか見ないかは議論があるでしょうが) で、まず式の部分を完成させるのが最初かと思われます。 どのくらいの複雑なものまで処理させるのかわかりませんが 1+2+3 が[:add, 1.0, 2.0]になります。 1+2*3 はうまく行くようですが 1*2+3 はだめ
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問