目次
前
次
制御構文仕様設計
開発しているBASICインタプリタでは、構造化プログラムを
書きやすいように、選択、反復には次の構文をサポートして
います。
- IF ... THEN ... ENDIF
- IF ... THEN ... ELSE ... ENDIF
- WHILE ... WEND
- FOR ... NEXT
各々の構文解析と制御方法を、予め決めておきます。
IF
IFを使った構文は、ENDIFで終了し、途中には
必ずTHENを入れているので、リスト中に3つの
トークン(TOKEN)が含まれていることを確認して
おきます。
ステートメントをリストで表現しておき、リストの
要素をサーチ。3つのトークンが含まれているかを
フラグの値に反映させます。3フラグの論理積が1
であれば、IFステートメントとしては正しいと判断。
ELSEが含まれているかもサーチして、含まれていれば
どの位置かを記憶しておきます。
IFステートメントとして、文法上正しいのかは判断し
各トークンの位置をリストで返す関数を定義して処理
を一任。
proc is_ifs {x} {
# copy
set xx $x
# initialize flag
set bflag -1
set fflag -1
set tflag -1
set eflag -1
# initialize location
set bl -1
set fl -1
set tl -1
set el -1
# loop
set i 0
foreach e $xx {
# judge
if { $e == "IF" } {
set bflag 1
set bl $i
}
if { $e == "ENDIF" } {
set fflag 1
set fl $i
}
if { $e == "THEN" } {
set tflag 1
set tl $i
}
if { $e == "ELSE" } {
set eflag 1
set el $i
}
# increment
incr i
}
#
set result [expr $bflag * $tflag * $fflag]
# concatenate
set result "$result $bl $tl $el $fl"
return $result
}
トークン位置がわかれば、条件、条件成立時のステートメント
条件不成立時のステートメントを抽出できます。
トークンのIFとTHENの間から、条件を抽出。
トークンのTHENとENDIFあるいはTHENとELSEの間から
条件成立時のステートメントを抽出。
トークンのELSEとENDIFの間から条件不成立時の
ステートメントを抽出。
ステートメントは、LET、PRINT、GOTO、GOSUB、RETURN、関数
を含んでいるので、個々の専用処理に渡し、IFステートメント
の処理を終了します。
WHILE ... WEND
WHILEを使った構文では、WHILEの右に条件が
置かれるので、条件成立で次の行からWENDを
見つけるまで、ステートメントを実行します。
条件不成立であれば、WENDのある行の次の行に
移動できるように、シーケンスカウンタの値を
設定。
WENDでは、WHILEを含んだ行に戻るために
シーケンスカウンタの値を設定するだけ。
WHILEで条件を取り出し、真偽を返す関数を定義します。
proc get_condition_value {x} {
# default
set result 0
#
set first [lindex $x 0]
set xop [lindex $x 1]
set second [lindex $x end]
# judge
if { $xop == "=" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car == $cdr } {
set result 1
}
}
if { $xop == "<>" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car != $cdr } {
set result 1
}
}
if { $xop == ">=" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car >= $cdr } {
set result 1
}
}
if { $xop == ">" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car > $cdr } {
set result 1
}
}
if { $xop == "<=" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car <= $cdr } {
set result 1
}
}
if { $xop == "<" } {
set car [get_value $first]
set cdr [get_value $second]
if { $car < $cdr } {
set result 1
}
}
return $result
}
偽は論理値の0、真は論理値の1を返します。
WHILEを含んだ行では、条件成立で行番号を保存し
WENDを含んだ行で参照可能に。
WHILEとWENDに挟まれた行番号のステートメントを
実行することを表現するため、フラグを利用。
このフラグにセマフォの役を与え、WENDでの処理
が単純になるようにしました。
WHILEを含んだ行では、条件成立で行番号を保存し
WENDを含んだ行で参照可能に。
セマフォフラグを使うと、WENDでの処理は単純です。
WENDを含んだ行では、セマフォフラグがセットされて
いれば、リセット。記憶しているWHILEを含んだ行の
行番号を返して終了。
FOR ... NEXT
FORを使った構文では、制御変数の初期化と
終了値を含んでいるので、これらを取得して
別に用意したカウンタでFORとNEXTに挟まれた
行番号のステートメントを実行。
FORを含んだステートメントが、必要なトークンを
含んでいるかを調べる関数を定義しておきます。
proc is_fors {x} {
# copy
set xx $x
# initialize flag
set bflag -1
set tflag -1
set eflag -1
# initialize location
set bl -1
set tl -1
set el -1
# loop
set i 0
foreach e $xx {
# judge
if { $e == "FOR" } {
set bflag 1
set bl $i
}
if { $e == "TO" } {
set tflag 1
set tl $i
}
if { $e == "STEP" } {
set eflag 1
set el $i
}
# increment
incr i
}
# ? correct FOR statement
if { $bflag == 1 && $tflag == 1 } {
set result 1
}
return "$result $bl $tl $el"
}
トークン位置がわかれば、制御変数、初期値、最終値、
制御変数の加算値を抽出できます。次のように判定。
トークンのFORとTOの間から、制御変数と初期値を抽出。
トークンのTOの次にある最終値を抽出。
トークンのSTEPの次にある制御変数の加算値を抽出。
抽出したパラメータは、リストにしておきます。
FORとTOの2つのトークンが存在していれば、フラグを
セット。2フラグの論理積を求め、FOR文として正しい
のかを論理値に反映させます。
パラメータと判定フラグをまとめてリストにし
呼び出し側への渡す情報とします。
FORとNEXTに挟まれた行番号のステートメントは
コマンドによる処理なので、専用処理ブロックに
渡して実行。
FORによる繰返しに対応したステートメントと判断
できるようにフラグを使います。
フラグはNEXTの処理で参照。
NEXTを含む行のステートメントでは、フラグを参照し
制御変数の値を更新。制御変数値が最終値を超えた時
FORを使ったループから抜けます。
制御変数値を更新したなら、変数あるいは配列の該当
するラベルアドレスの値も同時に更新。
制御変数値が最終値より小さいなら、FORを含む行に
もどり、ループ処理を続けます。
目次
前
次