目次

制御構文仕様設計

 開発しているBASICインタプリタでは、構造化プログラムを
 書きやすいように、選択、反復には次の構文をサポートして
 います。

 各々の構文解析と制御方法を、予め決めておきます。

 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を含む行に
  もどり、ループ処理を続けます。


目次

inserted by FC2 system