目次

インタプリタ詳細設計

 インタプリタを実現するための仕様が固まりました。

 ステートメントを実行するために必要となる
 手続き、関数を用意します。

 手続き、関数を思いつくままにリストしてから
 その仕様から詳細を設計していきます。

 各仕様を設計していきます。

 具体的なコードは、Tcl/Tkで記述します。


行初期化  インタプリタで扱える行数を決めておき  ある程度の規模のプログラムコード実行  に耐えられるようにします。  今回は、最大4096行まで扱えるとします。  初期化は、行番号とステートメントに分けます。  コードは、以下。 for {set i 0} {$i < 4096} {incr i} { set ls($i) -1 set ss($i) "" }  行番号の連想配列名をlsに、ステートメントの  連想配列名を、ssとします。  行番号をラベルと考えると、連想配列で管理しておけば  GOTO、GOSUB等の分岐で、分岐先のステートメントを取得  しやすくなると考えました。  行番号は1から始めるとして、行番号の連想配列には  存在しない行番号の「−1」を設定。
変数初期化  変数は'A'から'Z'の26文字なので、連想配列の  キーを文字'A'から'Z'のいずれかにして、ゼロ  で初期化。  コードは、以下。 for {set i 0} {$i < 26} {incr i} { # generate ascii code set j [format "%c" [expr $i+65]] # substitute set xvar($j) 0 }  変数格納用の連想配列名を、xvarとします。
配列初期化  配列は<0>から<100>の101個なので、連想  配列のキーを<0>から<100>のいずれかにし  ゼロで初期化。  コードは、以下。 for {set i 0} {$i < 101} {incr i} { # generate ascii code set j "\<$i\>" # substitue set xarray($j) 0 }  変数格納用の連想配列名を、xvarとします。
変数、配列、関数、手続きの判別  命令(コマンド)の中で、LET、PRINTは変数、配列、関数を扱い  IF文、WHILE文、FOR文では変数、配列、関数、手続きを扱います。  個別に判別して全体が膨れ上がるので、まとめます。  変数の判別は、文字'A'から'Z'のいずれかを判断し  変数であれば1を返し、それ以外は0を返すと考え  ます。 proc is_variable {x} { # default set result 0 # judge for {set i 0} {$i < 26} {incr i} { # generate string set symbol [format "%c" [expr $i+65]] # compare if { $symbol == $x } { set result 1 } } return $result }  配列の判別は、<0>から<100>のいずれかを  判断し、配列であれば1を返し、それ以外は0を返すと  考えます。 proc is_array {x} { # default set result 0 # judge for {set i 0} {$i < 101} {incr i} { # generate string set symbol "\<$i\>" # compare if { $symbol == $x } { set result 1 } } return $result }  変数、配列の判別ができたとして、実際は数値を利用するので  変数、配列に格納されている数値を取り出す処理が必要です。  次のように実現すればよさそう。 proc get_value {x} { # global xvar xarray io_in_v io_out_v # default set result $x # have operator if { [have_operator $x] > 0 } { set result [calc_asmdr $x] } # variable if { [is_variable $x] > 0 } { set result $xvar($x) } # array if { [is_array $x] > 0 } { set result $xarray($x) } # INP if { [have_inp $x] > 0 } { set result [get_decimal $io_in_v] } # IN if { [have_in $x] > 0 } { set result [in_handler $x] } return $result }  入力関数IN、INPを含めて、1ビットの論理値と8ビット  の10進数で数値を取得できるようにしています。  1ビットの論理値を取得する関数は、in_handler。  8ビットの10進数で数値を取得する関数として   inp_handler  を用意。  10進数への変換操作は、次の関数が担当。 proc get_decimal {x} { # copy set xx $x # clear set result 0 # add for {set i 0} {$i < 8} {incr i} { # get target set tmp [string index $xx $i] # add set result [expr $result * 2 + $tmp] } return $result }  数式を含む場合の判断も必要になるので、関数を2つ  定義します。  数式の演算記号を含むかを判断する関数は以下。 proc have_operator {x} { # default set result 0 # copy set xx $x # judge if { [llength [split $xx +]] > 1 } { set result 1 } if { [llength [split $xx -]] > 1 } { set result 1 } if { [llength [split $xx *]] > 1 } { set result 1 } if { [llength [split $xx /]] > 1 } { set result 1 } if { [llength [split $xx %]] > 1 } { set result 1 } if { [llength [split $xx &]] > 1 } { set result 1 } if { [llength [split $xx |]] > 1 } { set result 1 } if { [llength [split $xx ^]] > 1 } { set result 1 } return $result }  数式の計算を実行する関数は以下。 proc calc_asmdr {x} { # default set result $x # +(add) if { [llength [split $x +]] > 1 } { # split set xx [split $x +] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # add set result [expr $xcar + $xcdr] } # -(subtract) if { [llength [split $x -]] > 1 } { # split set xx [split $x -] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # subtract set result [expr $xcar - $xcdr] } # *(multiply) if { [llength [split $x *]] > 1 } { # split set xx [split $x *] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # multiply set result [expr $xcar * $xcdr] } # /(divide) if { [llength [split $x /]] > 1 } { # split set xx [split $x /] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # divide set result [expr $xcar / $xcdr] } # %(resident) if { [llength [split $x %]] > 1 } { # split set xx [split $x %] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # add set result [expr $xcar % $xcdr] } # &(logical and) if { [llength [split $x &]] > 1 } { # split set xx [split $x &] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # logical and set result [expr $xcar & $xcdr] } # |(logical or) if { [llength [split $x |]] > 1 } { # split set xx [split $x |] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # logical or set result [expr $xcar | $xcdr] } # ^(logical exclusive or) if { [llength [split $x ^]] > 1 } { # split set xx [split $x ^] # get parameters set y [get_calc_param $xx] # separate set xcar [lindex $y 0] set xcdr [lindex $y end] # logical exclusive or set result [expr $xcar ^ $xcdr] } return $result }  数式処理では、2項演算に限定して、リストから  2パラメータを取り出す関数を使います。 proc get_calc_param {x} { # first set car [lindex $x 0] # second set cdr [lindex $x end] # convert set xcar [get_value $car] set xcdr [get_value $cdr] # concatenate set result "$xcar $xcdr" return $result }  この関数が、加減乗除算と剰余算、論理演算で  利用する演算子を発見して、演算を一手にまと  めていることに。
関数IN  グローバル変数io_in_vに入力値が含まれるとします。  8ビットで表現した2進数の指定ビットから論理値を  取出してくる機能を持たせます。  IN(?)で利用するとして、?には数値、変数、配列、数式  を指定できます。 proc in_handler {x} { global io_in_v # get index set xx [string index $x 3] # judge if { $xx == "<" } { if { [string index $x 5] == ">" } { set xv [get_value [string range $x 3 5]] } else { if { [string index $x 6] == ">" } { set xv [get_value [string range $x 3 6]] } else { set xv [get_value [string range $x 3 7]] } } } else { set xv [get_value [string index $x 3]] } # copy set result [string index $io_in_v [expr 7-$xv]] return $result }  MSBの位置を7とするために、ビット位置を7から減算。  関数INは、単独で使われることはなく、LET、PRINT、条件文  に含まれます。
関数INP  8ビットで表現した2進数をそのまま返す仕様とします。  パラメータなしでINP()として、グローバル変数io_in_vの  格納値を返す仕様で定義。 proc inp_handler {} { # global io_in_v return [get_decimal $io_in_v] }  10進数変換の関数を用意しておきます。 proc get_decimal {x} { # copy set xx $x # clear set result 0 # add for {set i 0} {$i < 8} {incr i} { # get target set tmp [string index $xx $i] # add set result [expr $result * 2 + $tmp] } return $result }  関数INPは、単独で使われることはなく、LET、PRINT、条件文  に含まれます。
手続きOUT  グローバル変数io_out_vに出力値が含まれるとします。  ビット位置と論理値の2パラメータを指定。  OUT(x,y)のようにxとしてビット位置、yとして論理値を指定。  x、yには数値、変数、配列、数式を使えるとします。 proc out_handler {x} { # global io_out_v # get bit location set bx [get_value [lindex $x 0]] set bx [expr 7-$bx] # get bit value set bv [get_value [lindex $x end]] # set result "" for {set i 0} {$i < 8} {incr i} { # get target bit set bb [string index $io_out_v $i] # replace if { $i == $bx } { set bb $bv } # append set result "$result$bb" } # store set io_out_v $result }
手続きOUTP  グローバル変数io_out_vに値を設定すれば処理は終了。  パラメータとして、変数、配列、2進数、16進数を指定  できるようにします。  2進数は文字列ですが、16進数は#をつけて判断。 proc outp_handler {x} { # global io_out_v # copy set xx $x # judge binary if { [string index $xx 0] == "\'" } { set y [get_decimal [string range $xx 1 8]] set ud [dec2bin [expr $y / 16]] set ld [dec2bin [expr $y % 16]] set yy "$ud$ld" } # judge hexadecimal if { [string index $xx 0] == "#" } { set ud [hex2bin [string index $xx 1]] set ld [hex2bin [string index $xx 2]] set yy "$ud$ld" } # judge variable if { [is_variable $x] > 0 } { set y [get_value $x] set ud [dec2bin [expr $y / 16]] set ld [dec2bin [expr $y % 16]] set yy "$ud$ld" } # judge array if { [is_array $x] > 0 } { set y [get_value $x] set ud [dec2bin [expr $y / 16]] set ld [dec2bin [expr $y % 16]] set yy "$ud$ld" } # store set io_out_v $yy }  10進数から2進数に変換するため、関数dec2binを用意。 proc dec2bin {x} { # default set result "0000" # judge if { $x == 1 } { set result "0001" } if { $x == 2 } { set result "0010" } if { $x == 3 } { set result "0011" } if { $x == 4 } { set result "0100" } if { $x == 5 } { set result "0101" } if { $x == 6 } { set result "0110" } if { $x == 7 } { set result "0111" } if { $x == 8 } { set result "1000" } if { $x == 9 } { set result "1001" } if { $x == 10 } { set result "1010" } if { $x == 11 } { set result "1011" } if { $x == 12 } { set result "1100" } if { $x == 13 } { set result "1101" } if { $x == 14 } { set result "1110" } if { $x == 15 } { set result "1111" } return $result }  16進数から2進数に変換するため、関数hex2binも用意。 proc hex2bin {x} { # default set result "0000" # judge if { $x < 10 } { set result [dec2bin $x] } if { $x == "A" || $x == "a" } { set result "1010" } if { $x == "B" || $x == "b" } { set result "1011" } if { $x == "C" || $x == "c" } { set result "1100" } if { $x == "D" || $x == "d" } { set result "1101" } if { $x == "E" || $x == "e" } { set result "1110" } if { $x == "F" || $x == "f" } { set result "1111" } return $result }
比較演算  条件文の中で演算子の左右に、数値、変数、配列、関数の  出力値を置いて比較した結果、真偽を1、0で返します。  演算子の左右にあるパラメータを取り出し、数値にしてから  比較していきます。数値を取得するのに、関数get_valueを  使えばよいはず。 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 }  ここまでで、インタプリタの動作に必要な手続き、関数を  定義でたので、ステートメントとして利用できる命令を  確認しておきます。  ステートメントの先頭になる命令(コマンド)は以下。  関数IN、INPは、LETあるいはPRINTの中で  利用できる仕様です。

目次

inserted by FC2 system