目次

FORTHシステム(Python)

 新しいマイコンの勉強をする場合、アセンブリ言語しか
 用意されていない場合があります。

 アセンブリ言語のニモニックは覚えたり、マニュアルを調べるのが面倒です。
 より簡単な処理系を構成して、マイコンを使いやすくするので、簡易FORTH
 インタプリタを利用します。

 スタックを用意しておくと、複雑な計算も日本語に近い方式で書けます。
 また、proper programを書きやすくなるので、内蔵SRAMが小さなマイコン
 でも、小規模なOSなしのシステムを構築が可能です。

 C言語でいきなりFORTHシステムを作成するのは、開発時間が長くなるため
 Unix、Windowsの双方で使えるPythonで記述しました。

 コマンドラインから、コマンドとパラメータを
 入力するのは面倒なので、テキストファイルに
 次のような文字列を用意して確認することに。

1 2 + .
4 5 * .
10 1 2 - .
1 2 * 3 4 + .
9 2 / .
9 2 % .
100 10 inc .
100 22 dec .
1 dup . .
12 24 + . cr
1 2 3 4 drop drop . .
10 9 8 7 rot . . . .
1 << .
8 >> .
127 15 3 & .
128 16 3 | .
15 3 ^ .
10 1 < .
10 12 < .
10 10 < .
10 1 <= .
10 12 <= .
10 10 <= .
10 1 > .
10 12 > .
10 10 > .
10 1 >= .
10 12 >= .
10 10 >= .
10 1 == .
10 12 == .
10 10 == .
10 1 != .
10 12 != .
10 10 != .
10 12 < if 100 . then
10 12 > if 100 . else 200 . then
11 0 do I inc . loop
: mul3 3 * ;
: mulx 3 * 100 + ;
10 mul3 .
4 mulx .
1 2 3 40 swap . . . .
var xx 321 xx !
xx @ .
123 xx ! xx @ .
0 3 2 -5 min .
0 3 2 -5 max .

 作成したFORTHシステムで、動作確認すると、以下となりました。

> 1 2 + .
3
> 4 5 * .
20
> 10 1 2 - .
7
> 1 2 * 3 4 + .
7
> 9 2 / .
4
> 9 2 % .
1
> 100 10 inc .
11
> 100 22 dec .
21
> 1 dup . .
1
1
> 12 24 + . cr
36

> 1 2 3 4 drop drop . .
2
1
> 10 9 8 7 rot . . . .
8
9
7
10
> 1 << .
2
> 8 >> .
4
> 127 15 3 & .
3
> 128 16 3 | .
147
> 15 3 ^ .
12
> 10 1 < .
0
> 10 12 < .
1
> 10 10 < .
0
> 10 1 <= .
0
> 10 12 <= .
1
> 10 10 <= .
1
> 10 1 > .
1
> 10 12 > .
0
> 10 10 > .
0
> 10 1 >= .
1
> 10 12 >= .
0
> 10 10 >= .
1
> 10 1 == .
1
> 10 12 == .
12
> 10 10 == .
10
> 10 1 != .
1
> 10 12 != .
1
> 10 10 != .
0
> 10 12 < if 100 . then
100
> 10 12 > if 100 . else 200 . then
200
> 11 0 do I inc . loop
1
2
3
4
5
6
7
8
9
10
11
> : mul3 3 * ;
> : mulx 3 * 100 + ;
> 10 mul3 .
30
> 4 mulx .
112
> 1 2 3 40 swap . . . .
3
40
2
1
> var xx 321 xx !
> xx @ .
321
> 123 xx ! xx @ .
123
> 0 3 2 -5 min .
-5
> 0 3 2 -5 max .
3

 380行程度で処理系を記述できました。
 スクリプトは以下。

#! /usr/bin/python 

#++++++++++++++++++++++++++++
# ? context of command
#++++++++++++++++++++++++++++
def iscmd(x):
  global mfcmd
  # calculate
  result = x in mfcmd
  #
  return result

#++++++++++++++++++++++++++++
# relation computing
#++++++++++++++++++++++++++++
def isrelation(x):
  # default
  result = 0
  # judge size
  if len(x) == 3 :
    fx = int(x[0])
    sx = int(x[1])
    # < 
    if x[2] == '<' :
      if fx < sx :
        result = 1
    # <= 
    if x[2] == '<=' :
      if fx <= sx :
        result = 1
    # > 
    if x[2] == '>' :
      if fx > sx :
        result = 1
    # >= 
    if x[2] == '>=' :
      if fx >= sx :
        result = 1
    # == 
    if x[2] == '==' :
      if fx == sx :
        result = 1
    # != 
    if x[2] == '!=' :
      if fx != sx :
        result = 1

  return result

#++++++++++++++++++++++++++++
# user word
#++++++++++++++++++++++++++++
def isuserword(x):
  global wordlst
  # calculate
  result = x in wordlst
  #
  return result

#++++++++++++++++++++++++++++
# variable word
#++++++++++++++++++++++++++++
def isvar(x):
  global vlst
  # calculate
  result = x in vlst
  #
  return result

#++++++++++++++++++++++++++++
# intepreter
#++++++++++++++++++++++++++++
def interpret(x):
  global mydic,vdic,vlst
  # clear user stack
  ustack = []
  # show line
  print ">",x
  # clear
  result = ""
  # separate token
  xx = x.split(' ')
  # replace word
  yy = []
  for i in xx :
    # copy
    tmp = i
    # user word 
    if isuserword(i):
      # get word body
      tmp = mydic[i]
      # copy each element
      for j in tmp :
        yy.append(j)
    else :
      yy.append(tmp)
  # update
  xx = yy
  last = len(xx)
  #
  ptr = 0
  xbegin = 0
  xlast  = 0
  xcnt   = 0
  # scan
  while ptr < last :
    # get token
    i = xx[ptr]
    # poke
    if i.isdigit() == True :
      ustack.append(i)
      ptr += 1
    # is variable
    if isvar(i) == True :
      # poke
      ustack.append(i)
      ptr += 1
    # do command
    if iscmd(i) == True :
      ptr += 1
      # add
      if i == '+' :
        result = 0
        for e in ustack :
          result += int(e)
        ustack = []
        ustack.append(result)
      # sub
      if i == '-' :
        result = 2 * int(ustack[0])
        for e in ustack :
          result -= int(e)
        ustack = []
        ustack.append(result)
      # multipy
      if i == '*' :
        result = 1
        for e in ustack :
          result *= int(e)
        ustack = []
        ustack.append(result)
      # divide
      if i == '/' :
        result = int(ustack[0])
        if int(ustack[1]) != 0 :
          result /= int(ustack[1])
        ustack = []
        ustack.append(result)
      # remainder
      if i == '%' :
        result = int(ustack[0])
        if int(ustack[1]) != 0 :
          result %= int(ustack[1])
        ustack = []
        ustack.append(result)
      # .
      if i == '.' :
        result = ustack[-1]
        ustack = ustack[0:-1]
        print result
        result = ""
      # inc
      if i == 'inc' :
        result = int(ustack[-1])
        result = result + 1
        ustack = ustack[0:-1]
        ustack.append(result)
      # dec
      if i == 'dec' :
        result = int(ustack[-1])
        result = result - 1
        ustack = ustack[0:-1]
        ustack.append(result)
      # dup
      if i == 'dup' :
        result = int(ustack[-1])
        ustack.append(result)
      # cf
      if i == 'cr' :
        print
      # drop
      if i == 'drop' :
        ustack = ustack[0:-1]
      # rot
      if i == 'rot' :
        if len(ustack) >= 3 :
          fx = ustack[-1]
          sx = ustack[-2]
          tx = ustack[-3]
          ustack = ustack[0:-3]
          ustack.append(fx)
          ustack.append(tx)
          ustack.append(sx)
      # <<
      if i == '<<' :
        result = int(ustack[-1])
        result <<= 1
        ustack = ustack[0:-1]
        ustack.append(result)
      # >>
      if i == '>>' :
        result = int(ustack[-1])
        result >>= 1
        ustack = ustack[0:-1]
        ustack.append(result)
      # &
      if i == '&' :
        result = int(ustack[0])
        ustack = ustack[1:]
        for e in ustack :
          result = result & int(e)
        ustack = []
        ustack.append(result)
      # |
      if i == '|' :
        result = int(ustack[0])
        ustack = ustack[1:]
        for e in ustack :
          result = result | int(e)
        ustack = []
        ustack.append(result)
      # ^
      if i == '^' :
        result = int(ustack[0])
        ustack = ustack[1:]
        for e in ustack :
          result ^= int(e)
        ustack = []
        ustack.append(result)
      # < , <= , > , >=
      if i == '<' or i == '<=' or i == '>' or i == '>=' :
        if len(ustack) >= 2 :
          result = isrelation([ustack[-2],ustack[-1],i])
          ustack = ustack[:-3]
          ustack.append(result)
      # == , != 
      if i == '>=' or i == '!=' :
        if len(ustack) >= 2 :
          result = isrelation([ustack[-2],ustack[-1],i])
          ustack = ustack[:-3]
          ustack.append(result)
      # swap
      if i == 'swap' :
        if len(ustack) >= 2 :
          fx = ustack[-1]
          sx = ustack[-2]
          ustack = ustack[:-2]
          ustack.append(fx)
          ustack.append(sx)
      # if
      if i == 'if' :
        # judge
        if result == 1 :
          ptr = xx.index('if') + 1
        else :
          ptrelse = xx.index('else')
          if ptrelse > ptr :
            ptr = ptrelse
          ptrthen = xx.index('then')
          if ptrthen > ptr :
            ptr = ptrelse
      # then
      if i == 'then' :
        ptr += 1
      # do
      if i == 'do' :
        if len(ustack) >= 2 :
          ptrdo = xx.index('do')
          xbegin = int(xx[ptrdo-1])
          xlast  = int(xx[ptrdo-2])
          xcnt   = xbegin
          ptr = ptrdo + 1
      # I or i
      if i == 'I' or i == 'i' :
        ustack.append(xcnt)
      # loop
      if i == 'loop' :
        xcnt += 1
        if xcnt == xlast :
          ptr += 1
        if xcnt < xlast :
          ptrdo = xx.index('do')
          ptr = ptrdo + 1
      # define word :
      if i == ':' :
        # get word label
        ptrwds = xx.index(':')+1
        ptrwde = xx.index(';')
        wdn = xx[ptrwds]
        # word body
        wdbody = xx[ptrwds+1:ptrwde]
        # store
        wordlst.append(wdn)
        mydic[wdn] = wdbody
        # 
        ptr = ptrwde
      # define word ;
      if i == ';' :
        ptr += 1
      # define variable
      if i == 'var' :
        # get variable name
        varn = xx[xx.index('var')+1]
        # store variable name to list
        vlst.append(varn)
        # dummy
        vdic[varn] = 0
        # update 
        ptr += 2
      # get value form variable
      if i == '@' :
        # get variable name
        varn = xx[xx.index('@')-1]
        # poke
        ustack.append(vdic[varn])
        # update
        ptr = xx.index('@')+1
      # put value to variable
      if i == '!' :
        # get variable name
        varn = xx[xx.index('!')-1]
        # store
        vdic[varn] = xx[xx.index('!')-2]
        # update
        ptr = xx.index('!')+1
      # max
      if i == 'max' :
        ustack.append( max(ustack) )
        ptr = xx.index('max')+1
      # min
      if i == 'min' :
        ustack.append( min(ustack) )
        ptr = xx.index('min')+1

# open
fin = open('ftxt.txt','r')
# get parameters
line = fin.read()
dout = line.split('\n')
# close
fin.close()

# dictionary
mydic = {}
mydic["code"] = list('100 .')
wordlst = []
vlst = []
vdic = {}
vdic["aa"] = 0

mfcmd = ['+','-','*','/','%','.','inc','dec','dup','cr']
mfcmd.append('drop')
mfcmd.append('rot')
mfcmd.append('swap')
mfcmd.append('<<')
mfcmd.append('>>')
mfcmd.append('&')
mfcmd.append('|')
mfcmd.append('^')
mfcmd.append('<')
mfcmd.append('<=')
mfcmd.append('>')
mfcmd.append('>=')
mfcmd.append('==')
mfcmd.append('!=')
mfcmd.append('swap')
mfcmd.append('if')
mfcmd.append('else')
mfcmd.append('then')
mfcmd.append('do')
mfcmd.append('I')
mfcmd.append('i')
mfcmd.append('loop')
mfcmd.append(':')
mfcmd.append(';')
mfcmd.append('var')
mfcmd.append('!')
mfcmd.append('@')
mfcmd.append('max')
mfcmd.append('min')
for e in dout :
  interpret(e)

 built-inの命令(FORTHではワード)を用意し、選択は
 if...else...then、繰返しはdo...loopだけにしました。
 時間をかければ、ビットごとの論理演算、変数名への代入
 参照等の機能を付加できます。

 built-inのワードは、以下。

 シェルに相当するコマンドインタプリタは
 次のシーケンスで動かしてみました。
  1. バッファから1行取得
  2. トークンに分割
  3. トークンが数値なら、スタックに転送
  4. トークンがユーザー定義ワードなら、展開してbuilt-inワードのシーケンスに変換
  5. ユーザーのワード定義指定があれば、専用エリアにbuilt-inワードのシーケンスに変換
  6. ワードを実行
  7. 1に戻る
 シェル相当のコマンドインタプリタは、プロシージャ  「interpret」にまとめました。  プロシージャ「interpret」内で1行をトークンに分割し  数値であればスタックに積み、演算指定のトークンなら  スタックから数値を取り出して計算、ユーザー定義の  ワードならば展開します。  数値をスタックに積むことと演算指定のトークンの処理の  前に、ユーザー定義ワードを展開しました。  スタックは、データ構造としてリストを採用。  1行をリストで表現すると、トークンの判別が  簡便にできました。  ユーザー定義ワードは、辞書の中にワード名と  処理シーケンスをペアで格納します。  イメージは、次のようになります。 "mul10" -> 'mul10':['10','*'] "mulx" -> 'mulx':['2','*','1','+']  ユーザー定義ワードを使う場合、リストを利用して  該当するシーケンスに置換するので、マクロを使う  イメージとなります。  構文解析は、トークンスキャナーを定義しトークンを逐次  調べて解釈し、計算します。ユーザー定義ワードを、発見  したなら、展開置換します。  構文解析のプロシージャは、以下。 def interpret(x): global mydic,vdic,vlst # clear user stack ustack = [] # show line print ">",x # clear result = "" # separate token xx = x.split(' ') # replace word yy = [] for i in xx : # copy tmp = i # user word if isuserword(i): # get word body tmp = mydic[i] # copy each element for j in tmp : yy.append(j) else : yy.append(tmp) # update xx = yy last = len(xx) # ptr = 0 xbegin = 0 xlast = 0 xcnt = 0 # scan while ptr < last : # get token i = xx[ptr] # poke if i.isdigit() == True : ustack.append(i) ptr += 1 # is variable if isvar(i) == True : # poke ustack.append(i) ptr += 1 # do command if iscmd(i) == True : ptr += 1 # add if i == '+' : result = 0 for e in ustack : result += int(e) ustack = [] ustack.append(result) # sub if i == '-' : result = 2 * int(ustack[0]) for e in ustack : result -= int(e) ustack = [] ustack.append(result) # multipy if i == '*' : result = 1 for e in ustack : result *= int(e) ustack = [] ustack.append(result) # divide if i == '/' : result = int(ustack[0]) if int(ustack[1]) != 0 : result /= int(ustack[1]) ustack = [] ustack.append(result) # remainder if i == '%' : result = int(ustack[0]) if int(ustack[1]) != 0 : result %= int(ustack[1]) ustack = [] ustack.append(result) # . if i == '.' : result = ustack[-1] ustack = ustack[0:-1] print result result = "" # inc if i == 'inc' : result = int(ustack[-1]) result = result + 1 ustack = ustack[0:-1] ustack.append(result) # dec if i == 'dec' : result = int(ustack[-1]) result = result - 1 ustack = ustack[0:-1] ustack.append(result) # dup if i == 'dup' : result = int(ustack[-1]) ustack.append(result) # cf if i == 'cr' : print # drop if i == 'drop' : ustack = ustack[0:-1] # rot if i == 'rot' : if len(ustack) >= 3 : fx = ustack[-1] sx = ustack[-2] tx = ustack[-3] ustack = ustack[0:-3] ustack.append(fx) ustack.append(tx) ustack.append(sx) # << if i == '<<' : result = int(ustack[-1]) result <<= 1 ustack = ustack[0:-1] ustack.append(result) # >> if i == '>>' : result = int(ustack[-1]) result >>= 1 ustack = ustack[0:-1] ustack.append(result) # & if i == '&' : result = int(ustack[0]) ustack = ustack[1:] for e in ustack : result = result & int(e) ustack = [] ustack.append(result) # | if i == '|' : result = int(ustack[0]) ustack = ustack[1:] for e in ustack : result = result | int(e) ustack = [] ustack.append(result) # ^ if i == '^' : result = int(ustack[0]) ustack = ustack[1:] for e in ustack : result ^= int(e) ustack = [] ustack.append(result) # < , <= , > , >= if i == '<' or i == '<=' or i == '>' or i == '>=' : if len(ustack) >= 2 : result = isrelation([ustack[-2],ustack[-1],i]) ustack = ustack[:-3] ustack.append(result) # == , != if i == '>=' or i == '!=' : if len(ustack) >= 2 : result = isrelation([ustack[-2],ustack[-1],i]) ustack = ustack[:-3] ustack.append(result) # swap if i == 'swap' : if len(ustack) >= 2 : fx = ustack[-1] sx = ustack[-2] ustack = ustack[:-2] ustack.append(fx) ustack.append(sx) # if if i == 'if' : # judge if result == 1 : ptr = xx.index('if') + 1 else : ptrelse = xx.index('else') if ptrelse > ptr : ptr = ptrelse ptrthen = xx.index('then') if ptrthen > ptr : ptr = ptrelse # then if i == 'then' : ptr += 1 # do if i == 'do' : if len(ustack) >= 2 : ptrdo = xx.index('do') xbegin = int(xx[ptrdo-1]) xlast = int(xx[ptrdo-2]) xcnt = xbegin ptr = ptrdo + 1 # I or i if i == 'I' or i == 'i' : ustack.append(xcnt) # loop if i == 'loop' : xcnt += 1 if xcnt == xlast : ptr += 1 if xcnt < xlast : ptrdo = xx.index('do') ptr = ptrdo + 1 # define word : if i == ':' : # get word label ptrwds = xx.index(':')+1 ptrwde = xx.index(';') wdn = xx[ptrwds] # word body wdbody = xx[ptrwds+1:ptrwde] # store wordlst.append(wdn) mydic[wdn] = wdbody # ptr = ptrwde # define word ; if i == ';' : ptr += 1 # define variable if i == 'var' : # get variable name varn = xx[xx.index('var')+1] # store variable name to list vlst.append(varn) # dummy vdic[varn] = 0 # update ptr += 2 # get value form variable if i == '@' : # get variable name varn = xx[xx.index('@')-1] # poke ustack.append(vdic[varn]) # update ptr = xx.index('@')+1 # put value to variable if i == '!' : # get variable name varn = xx[xx.index('!')-1] # store vdic[varn] = xx[xx.index('!')-2] # update ptr = xx.index('!')+1 # max if i == 'max' : ustack.append( max(ustack) ) ptr = xx.index('max')+1 # min if i == 'min' : ustack.append( min(ustack) ) ptr = xx.index('min')+1  1行をリストとみなして、トークンに分割します。  if...else...thenは、比較処理の結果を1か0にして  スタックに積みます。1では、ifに連なるブロックを  0では、elseに連なるブロックを実行します。  数値を出し入れするスタックは、インタプリタ内部で  用意しているので、インタプリタが動作するときだけ  メモリが増えることに。  do...loopでは、doの位置で初期値、終了値をスタック  から取出します。初期値を内部カウンタに設定しloopの  位置で、内部カウンタを更新。内部カウンタ値が終了値  ならば終了します。内部カウンタが最終値未満ならdoの  右に連なるブロックを実行。これを繰り返します。  do...loopのdo、loopは、制御で必要になる初期化、判定  制御変数更新のタイミングを指定するラベルと考えました。  計算と比較処理は、シーケンスで利用する文字列さえ  判断できれば、ひとつのブロックにまとめられますが  可読性が悪くなるので、分割して記述しています。  Unix系OSでは、Pythonが標準バンドルなのでMacOSXで作成し  Windowsで動作確認しました。

目次

inserted by FC2 system