パソコン活用研究5番街(Visual
Basic、Excel(VBA)、BASIC プログラミング研究)
VB.NETで計算機を作る(乗算・除算)
(準備中)
前回(「VB.Netで計算機を作る(足し算引き算)」)では、再帰下降パースによる足し算引き算の実装をして
みましたが、今回は乗算(掛け算)・除算(割り算)と括弧( )を使えるようにしてみます。
Cによる完成形のプログラムは「プログラミング言語を作る」のP57〜P70にあります。
本に掲載されているプログラムは、再帰下降パーサによる完成されたプログラムで、
字句解析ー>構文解析ー>演算まで2段抜かしでやってしまうようなプログラムです。
このページでは、各プロセスの動きを1段づつ確認したいので、本にあるような洗練されたものとはかなり違う
無骨なプログラムを作っていきます。各プロセスのアルゴリズム、動作の確認を主眼においているので、
正しくない式が入力された時のチェック(異常系のチェック)は甘くしてます。なので正常系の場合のみちゃんと動作します。

If t.type = type.lparen Then
v = expression()
t = get_token()
If t.type = type.rparen Then Return v Else Console.WriteLine("rparen is missing")
End If
Cunget_token
今読んでいるtokenの番号をひとつ戻します。
2*(1+-3)の演算をしてみた時の動作をトレースしてみます。
Bで( )の処理に入ります。
DとFのプロセスが少し複雑な動きです。


B以降がやや複雑なので動作を説明します。( )の再帰的な処理と、unget_tokenでtokenを式に戻す
あたりがなかなかわかりにくい個所です。
unget_tokenでtokenを式に戻す動作については、実際は今読んでいるtoken番号をひとつ減らすという
操作で行っています。
Aでは、関数termのなかのv2=primary_expresiionのところまで進んでいます。
B primary_expressionの中で、左括弧"("が登場すると、expressionを再帰的に呼び出します。
C 再帰的に呼び出されたexpressionの中で、1+-3 の処理に入ります。
D termの中で、"+"が出てきたときは、unget_tokenして"+"は式の中に戻します。
whileループを抜けて、値1をexpressionにリターンします。
expressionの中であらためて"+"を取り出します。
E expressionの中でv2取得のプロセスに入り、expression -> term ->primary
expression -> get_token
-> primary_expressionで-3を取得 -> termのv1に-3を取得
F termの演算子チェックで右括弧")"に出会う -> unret_tokenで")"を式に戻し、whileループを抜けtermは
expressionに-3をリターン。
expressionは 1+-3を計算し、演算結果として-2を得る -> whileループの中でget_tokenで")"を得る。
->unget_tokenで")"を式に戻しwhileループを抜ける ->
Bで呼び出された ( )の結果として-2をリターン
-> ( )の処理を終える。
primary_expressionはBでexpressionを呼び出した次のコードの実行に移る。->
get_tokenで")"を取得したので、-2を
リターン -> termに戻りtermののv2=-2が取得される。
G termでv1*v2の計算が行われ、結果として-4が得られる。 ->whileループのget_tokenで"line_end"を取得
->whileループを抜け、-4をexpressionにリターン
@〜Gの動作フローを図示してえみます。各関数が自分の受け持った処理だけを行い、他の処理は他の関数
に丸投げしていますが、非常に巧妙に動作していることがわかると思います。

( )が複数重なってもちゃんと処理できます。
ちなみに演算用の変数v1,v2はdouble型ですので演算結果は小数点が出てきても大丈夫です。
2*(5/(1+2))+2 を演算させてみた結果
(途中はいくらか省略しています)

ソースコード(変更、追加のあった個所のみ)
Function expression() As Double
Dim v1, v2 As Double
Dim t As token
v1 = term()
Console.WriteLine("expression v1:{0}", v1)
While (1)
t = get_token()
Console.WriteLine("expression {0}: {1} : {2} ", t.str, t.type, t.v)
If (t.type <> type.add_op And t.type <> type.sub_op) Then unget_token() : Exit While
v2 = term()
Console.WriteLine("expression v2:{0}", v2)
If t.type = type.add_op Then v1 = v1 + v2
If t.type = type.sub_op Then v1 = v1 - v2
Console.WriteLine("expression calculated v1:{0}", v1)
End While
Return v1
End Function
Function term() As Double
Dim v1, v2 As Double
Dim t As token
v1 = primary_expression()
Console.WriteLine("term v1:{0}", v1)
While (1)
t = get_token()
Console.WriteLine("term {0}: {1} : {2} ", t.str, t.type, t.v)
If (t.type <> type.mul_op And t.type <> type.div_op) Then unget_token() : Exit While
v2 = primary_expression()
Console.WriteLine("term v2:{0}", v2)
If t.type = type.mul_op Then v1 = v1 * v2
If t.type = type.div_op Then v1 = v1 / v2
Console.WriteLine("term calculated v1:{0}", v1)
End While
Return v1
End Function
Function primary_expression() As Double
Dim t As token
Dim v As Double
t = get_token()
Console.WriteLine("primary_expression {0}: {1} : {2} ", t.str, t.type, t.v)
If t.type = type.number Then Return t.v
If t.type = type.lparen Then
v = expression()
t = get_token()
If t.type = type.rparen Then Return v Else Console.WriteLine("rparen is missing")
End If
End Function
Function get_token() As token
Static Dim i As Integer = 0
REM t = tok(i)
If unget = 1 Then i = i - 1 : unget = 0
Console.WriteLine("get_token {0}: {1} : {2} :{3} ", i, tok(i).str, tok(i).type, tok(i).v)
REM Console.WriteLine("get_token {0}: {1} : {2} :{3} ", num_token, t.str, t.type, t.v)
i = i + 1
Return tok(i - 1)
End Function
Sub unget_token()
unget = 1
End Sub
TopPage > Visual BasIc&Excel活用研究目次