パソコン活用研究5番街(Visual Basic、Excel(VBA)、BASIC プログラミング研究)


VB.NETで計算機を作る(字句解析)
(準備中)

これはBASICで計算機を作る(字句解析)のVB.NET版です。
基本的に自分の勉強用なのでコードはボロです。異常値のチェックなどは甘々です。


VB.NET版では入力の規則として以下の3つの入力を想定しました。
@ 0〜9の数値
A 演算子として+,-.*,/
B ( ) 括弧

その他のルールとして空白は読みとばす、上記のルールにはずれた文字に出くわしたときは字句解析を中止

ただし、計算機のアルゴリズムの理解を目的にしているので、異常値の入力チェックはあまりしていません。
基本的に正常値が入力されることが前提です。

従って、BASIC版(BASICで計算機を作る(字句解析)では、-0, +0,00というような表記も排除していましたが、
VB.Net版は例えば0が先頭にくる場合のチェックは省略したので、+0は+を無視して0としてトークン化しますが、
-0や000, 001などはそのままトークン化してしまいます。

トークン化の判断は具体的には以下のルールで行っています。
isnum 現在処理中の文字が数値の一部かどうかの状態をあらわすフラグ。
isnum=0 現在処理している文字は数値の一部ではない
isnum=1 現在処理している文字は数値の一部である

変数posをトークンの数を保持するために使っている。

isnum=0 の時                          isnum=1 の時                        
数値 0〜9 数値の始まりとして認識し、isnum=1に変更する 数値の続きとして認識する
- 負の記号として認識し、数値の始まりとしてisnum=1
に変更する
数値が終了したとみなし、posを一つ加算し、演算子
としてトークン化する。更にposを一つ加算してisnum=0に変更
+ * / 無視 数値が終了したとみなし、posを一つ加算し、演算子
としてトークン化する。更にposを一つ加算してisnum=0に変更
( (としてトークン化し、posを1つ加算する。 無視
) 無視 数値の終了とみなし、posをひとつ加算する。
)としてトークン化する


上述したように異常値チェックはかなり甘いので、00や001のような表記も許容するし、
isnum=0の時に現れる+ * /は無視するので、1++2のような式は1+2の式として認識する。


以下、VB.NET版特有な個所についての説明。

列挙型
VB.NETの列挙型は以下のように書きます。
Enum クラス名
....
....
End Enum

列挙型に書かれた要素には0から順に数値が自動付与されます。(デフォルト動作)
下記のコードの場合、number=0, add_op=1 が自動付与されています。

列挙型の要素を参照する場合は
 列挙型のクラス名.要素
とします。
下記のコードの場合は、
type.number, type.add.op というようになります。

C言語の場合は、列挙型の要素を参照する場合は、要素だけを書けばOKなので
ここに違いがあります。


構造体
構造体は以下のように書きます。
Structure 構造体名
...
...
End Structure



入力された文字列(式)から1文字づつ取り出す。
statement.Chars(i)
で文字列statementからi番目の文字を取り出すことができます。iは0から始まりますので
i=2は3文字目。


Forループから一気に抜け出す場合は、Exit Forを使う(Cだとbreak)


Imports System

Module Program

Enum type
number
add_op
sub_op
mul_op
div_op
lparen
rparen
space
bad
End Enum

Structure token
Dim type As type
Dim str As String
Dim v As Integer
End Structure

Sub Main(args As String())
Dim i, pos, isnum As Integer
Dim r As type
Dim statement As String
Dim token(20) As token

statement = Console.ReadLine()

For i = 0 To statement.Length - 1
Console.WriteLine("{0} : {1}", i, statement.Chars(i))
Next

Console.WriteLine("TOKEN")
pos = 0 : isnum = 0
For i = 0 To statement.Length - 1
r = checktype(statement.Chars(i))
If r = type.space Then GoTo L1 :
If r = type.bad Then Exit For
If isnum = 1 And r = type.number Then token(pos).str = token(pos).str + statement.Chars(i) : token(pos).v = Integer.Parse(token(pos).str) : GoTo L1
If isnum = 1 And r = type.rparen Then pos = pos + 1 : token(pos).str = statement.Chars(i) : token(pos).type = r : GoTo L1
If isnum = 1 And (r = type.add_op Or r = type.sub_op Or r = type.mul_op Or r = type.div_op) Then pos = pos + 1 : token(pos).str = statement.Chars(i) : token(pos).type = r : pos = pos + 1 : isnum = 0 : GoTo L1
If isnum = 0 And r = type.lparen Then token(pos).str = token(pos).str + statement.Chars(i) : token(pos).type = r : pos = pos + 1 : GoTo L1
If isnum = 0 And r = type.number Then token(pos).str = token(pos).str + statement.Chars(i) : token(pos).type = type.number : token(pos).v = Integer.Parse(token(pos).str) : isnum = 1
If isnum = 0 And r = type.sub_op Then token(pos).str = token(pos).str + statement.Chars(i) : token(pos).type = type.number : isnum = 1
L1:
Next

For i = 0 To pos
Console.WriteLine("{0}: {1} : {2}", i, token(i).str, token(i).type)
Next

If r = type.bad Then Console.WriteLine("Wrong expression")
End Sub

Function checktype(ByVal t As String) As Integer
If IsNumeric(t) = True Then Return type.number
If t = "+" Then Return type.add_op
If t = "-" Then Return type.sub_op
If t = "*" Then Return type.mul_op
If t = "/" Then Return type.div_op
If t = "(" Then Return type.lparen
If t = ")" Then Return type.rparen
If t = " " Then Return type.space
Return type.bad

End Function
End Module





TopPage > Visual BasIc&Excel活用研究目次