目次
前
次
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のワードは、以下。
- +
- -
- *
- /
- %
- .
- inc
- dec
- dup
- cr
- drop
- rot
- swap
- <<
- >>
- &
- |
- ^
- <
- <=
- >
- >=
- ==
- !=
- swap
- if
- else
- then
- do
- I
- i
- loop
- :
- ;
- var
- !
- @
- max
- min
シェルに相当するコマンドインタプリタは
次のシーケンスで動かしてみました。
- バッファから1行取得
- トークンに分割
- トークンが数値なら、スタックに転送
- トークンがユーザー定義ワードなら、展開してbuilt-inワードのシーケンスに変換
- ユーザーのワード定義指定があれば、専用エリアにbuilt-inワードのシーケンスに変換
- ワードを実行
- 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で動作確認しました。
目次
前
次