FoRTH Modoki

Index > 趣味 > Computer > FoRTH Modoki

はじめに

C 言語で作っていた FORTH もどき言語(frtm) を Java に移植し,Java のス タンドアロンのアプリケーションとして動作するようにしました.その際に, 実数の計算に対応した他,三角関数等の算術関数のワードを追加しました.た だし,C 言語の UNIX+X11 版等にあったグラフィクス関係のワードは実装して おりません.

更に,Android OS 用のアプリケーション化も行いました.こちらは Google Play において有料で公開しています.下記のリンクからアクセスしてください.

Android アプリの公式開発言語が Java から Kotlin に変わったということで, Java から Kotlin への移植も行いました.Ver. 1.7.0 としましたが,BigInteger を使った分数(有理数)の計算を組み込みました.

ソースコード及び Applet, JAR

Java アプリケーション版のソースコードは下記のリンクから入手できます.gzip で圧縮してあります.ライセンスは GPL です.Java 版を簡単に試験できる Applet 版もあります.Kotlin 版の JAR も公開しました.

Android OS 用アプリケーション

Kotlin アプリケーション版に,Android OS 独自の機能を追加して Android OS 4.0 以上対応のスマートフォン用のアプリケーションを作成しました.言語の コアの部分は上の JAR のものと変わりありません.

Android 用に追加している機能は次の通りです.

  1. スタックと辞書の初期サイズの設定
  2. 辞書のワードをワードブックに保存する機能.ただしワードブックは3つまでに限定されています.
  3. 表示する文字のサイズを3段階から選ぶ機能
  4. 画面をワードの評価毎に初期化するモードと、画面を残すモードを選択する機能

Ver 1.6.0 からグラフィクス機能を復活させました.次のコマンドが利用可能です.

Ver 1.6.1 でグラフィクス用のワードの名称を短いものに変更しました.

gm		グラフィクス・モードへ
tm		テキスト・モードへ
x y moveto	(x, y) へ移動
x y lineto	現在位置から (x, y) まで直線描画
x y rmoveto	現在位置から (x, y) だけ移動
x y rlineto	現在位置から (x, y) だけ直線描画
x y w h rect	矩形領域の描画
x y w h rectf	矩形領域の塗り潰し
x y point	点の描画
x y w h ellips	楕円の描画
x y w h ellipsf	楕円の塗り潰し
c setcol	色設定(0: 黒 〜青緑水赤紫黄〜 15: 白) ** setcolor から改称 **
h l s setHLS	HLS 表色系で色指定(h 0:赤〜120:緑,240:青〜359, l,s: 0 〜 100)
n setfsz	フォントのサイズ指定 ** setfsize から改称 **
x y s drawstr	(x, y) に文字列 s を表示

□ 例題
・ 色を変えながら矩形領域を塗り潰す.
: boxes 16 0 for I setcol 10 20 I * + dup 100 100 rectf loop ;
gm boxes

: hls 16 0 for I 45 * 2 / 50 100 setHLS 10 20 I * + dup 100 100 rectf loop ;
gm hls

・再帰図形(Koch 曲線)
: pre 5 copy drop ;
: genx 3 ndup 6 ndup - * minus 100 / swap 4 ndup 7 ndup - * 100 / + 5 ndup +
  5 nrotr 4 ndrop ;
: geny swap 3 ndup 6 ndup - * 100 / swap 4 ndup 7 ndup - * 100 / + 4 ndup +
  5 nrotr 4 ndrop ;
: gen 6 copy genx 7 nrotr geny ;
: keep 7 nrotr 7 nrotr pre ;
: post 9 nrot 9 nrot 4 nrot 4 nrot 5 ndup 1- ;
: line moveto lineto ;
: koch dup 0= if drop line else 
    pre 0 0 gen keep 33 0 gen post koch
    pre 33 0 gen keep 50 -28 gen post koch
    pre 50 -28 gen keep 66 0 gen post koch
    pre 66 0 gen keep 100 0 gen post koch
    5 ndrop
  endif ;
: kochs 10 setcol  89 440 551 440 4 koch
        11 setcol 551 440 320  40 4 koch
        12 setcol 320  40  89 440 4 koch ;
gm kochs

内部では ReGIS graphics 端末のエスケープシーケンスを部分的に実装して いるため,グラフィクスモードに移行後,コマンドを直接出力することで作画 を行うことができます.

gm 
(W\(I15\)P[50,50]C\(S\)[][+40,+40][+40,-40][+40,+40][+40,-40][+40,+40][+40,-40][]\(E\)) .
(W\(I12\)P[50,150]F\(C\(B\)[200,+200][+200,-150]\(E\)\)) .
(W\(I11\)P[70,170]T\(S16\)[16,12]'Hello World') . 

grdemo というワードを使って,グラフィクスのサンプル用のワードをユー ザー辞書に追加することができます.

ドキュメント


FoRTh Modoki on Kotlin
ver 1.7.0  Mar. 3, 2019. 
Copyright (C) 1999-2019  浪花 智英 (Tomohide Naniwa)
naniwa@rbt.his.u-fukui.ac.jp

I. はじめに
このプログラムは FORTH を模擬したソフトですが,完全に FORTH を再現した
ものではありません.コンパイラは持ちませんが,ユーザー定義ワードでは定
数やワードを定義時に解釈します.

式の解釈は逆ポーランド記法に従って行われます.if による条件分岐.
do-while, for-loop などのループのための構造もあります.また,ワードを
組み合わせて新たなワードを作ることもできます.

スタックには整数および実数と,文字列を直接保存します.数字の入力は 10 
進数,16 進数(頭に0x を付ける),8 進数(頭に 0 を付ける)で行えます.

frtm で使用される各種スタックのサイズ等は自動的に拡大しますが,適切な
初期値を与える事で実行時の効率をあげる事ができます.初期値は次の通りと
なっています.
STMAX 200    ; 数値計算用スタック
CSMAX 200    ; ユーザー定義ワードの本体の記憶領域
コンストラクターの引数を変更する事で,適宜変更してください.

II. 基本機能
□ 定義済ワード
.	表示
.h	16 進表示
.o	8 進表示
cr	改行表示
sp	空白表示
+	和
-	差
*	積
/	商
//	有理数を作る
%	余り(整数のみ対応)
=	同値
<	より小さい
<=	以下
>	より大きい
>=	以上
!	真偽反転
1+	1 加算
1-	1 減算
0=	0 と同値
abs	絶対値
max	2数の最大
min	2数の最小
dup	複製
swap	交換
drop	除去
over	スタックの2番目の複製
rot	スタックの上位3つを回転し,3番目をトップへ
rotr	スタックの上位3つを回転し,トップを3番目へ
ndup	スタックの n 番目を複製
nrot	スタックを回転し,n 番目をトップへ
nrotr	スタックを回転し,トップを n 番目へ
ndrop	スタックの上位 n 個を除去
copy	スタックの上位 n 個を複製
SP	呼び出した時点のスタックの深さをスタックのトップに追加
minus	符合変換
spaces  複数個の空白表示
help	定義ワード一覧表示
%PI	円周率
%E	自然対数の底
sin	正弦
cos	余弦
tan	正接
asin	逆正弦
acos	逆余弦
exp	自然対数の底の巾乗
ln	自然対数
log	常用対数
sqrt	平方根
ceil	切り上げて整数に
floor	切捨てて整数に
round	四捨五入して整数に
deg	度へ
rad	ラジアンへ
atan2	逆正接
pow	巾乗
sinh	双曲線正弦
cosh	双曲線余弦
tanh	双曲線正接
asinh	双曲線逆正弦
acosh	双曲線逆余弦
atanh	双曲線逆正接
recip	逆数
fact	階乗
permut	順列
combin	組み合わせ
and	Bit AND
or	Bit OR
not	Bit 反転
xor	Bit 排他OR
#	直前に ., .o, .h で出力した値をスタックに積む
quit	終了

□ 文字列に対する処理
◇ 表示 echo
(Hello_World) echo cr

echo は以前のバージョンとの互換のためのワードで,文字列の表示も '.' に
よって行える.

文字列中で \ (バックスラッシュ)によるコントロール・コードのエスケープ
が可能である.利用可能な文字定数は '\n'(改行),'\t'(水平タブ), '\b'(バッ
クスペース)と '\ddd' (ddd は8進数) である.ただし,8進数の文字定数で
は最上位の数が 0 であることを仮定している.尚,これ以外の文字はそのま
ま文字列中に追加される.

◇ 文字列の分解・結合 pack/unpack
(Hello_World) unpack 
スタック上の文字列を1文字ずつに分解する.スタックには
    0 'd' 'l' 'r' 'o' 'W' '_' 'o' 'l' 'l' 'e' 'H'
と分解された整数が入る.

pack
unpack で1文字ずつに分解されたスタック上の整数を,再び文字列にパックする.

□ 有理数の計算
数値の計算の際には,次の優先順位で数値の型を自動で変換して計算を行う.

実数 > 有理数 > 整数

有理数の分子が分母より大きくなっても,帯分数にはしない.また,分母が 1
となった場合にも,整数に戻す処理は行なわない.尚、分子・分母は Java
(Kotlin) の BigInteger 型で表現されている.分母を 1 としておくことで,
無限長精度整数とみなして計算を行うこともできる.

例題
1 3 // 1 6 // + . cr
1 3 // 1 6 // - . cr
1 2 // 1 4 // * . cr
1 2 // 1 4 // / . cr

III. プログラム構造
□ 条件分岐
<cond> if <block 1> endif
<cond> if <block 1> else <block 2> endif

仕様
<cond> が 0 以外なら <block 1> を実行.
<cond> が 0 なら <block 2> を実行.

制限

else ブロックは複数書ける.

例題
0 if 1 else 2 endif . cr

□ do ループ
do <block 1> <cond> while

仕様
<cond> が 0 であれば終了.0 以外なら do に戻って繰り返し.

例題
1 do dup . cr 1 + dup 5 < while drop
1 do dup . sp 1 do dup . sp 1 + dup 5 < while cr drop 1 + dup 4 < while drop

□ for ループ
<n1> <n2> for <block> loop
<n1> <n2> for <block> <n3> +loop

仕様
<block> 終了後 n2 + 1(またはn3) を実行し,n1 より小さければ for に戻っ
て繰り返し.

I リターンスタック上の n2 をスタックに追加する.

制限
n1, n2 は非負の数とする.

例題
5 0 for I . cr loop
10 0 for I . sp 5 1 for I . sp loop cr 2 +loop

□ ワードの定義
: name <block> ;

仕様
name を持つワードを作成する.

制限
do や for のループの中ではワードの登録はできない.

ユーザー辞書は新しく定義されたものから順に検索するため,再帰が可能になっ
ている.また,ワードを再定義しても古いワードの定義はそのまま残る.

□ 例題
・平均
: ave + 2 / ;
10 28 ave . cr

・n 個の平均
: nave swap over 1 for rot + loop swap 1.0 * / ;
1 2 3 4 5 5 nave . cr 

・n 個の母分散
: nvarp dup 1+ copy nave SP nrotr swap dup * over 1 for rot dup * + loop
swap 1.0 * / SP nrot dup * - ;
1 2 3 4 5 5 nvarp . cr

・n 個の不偏分散
: nvar dup 1+ copy nave SP nrotr swap dup * over 1 for rot dup * + loop
SP nrot dup * 3 ndup * - swap 1- 1.0 * / ;
1 2 3 4 5 5 nvar . cr

・最大公約数
: gcd abs swap abs do swap over % dup 0 > while drop ;
1001 819 gcd . cr

・階乗 [fact(1) = 1, fact(n) = n*fact(n-1)]
: facti 1 swap 1+ 1 for I * loop ;
: factr dup 1 = if else dup 1- factr * endif ;
10 facti . cr
10 factr . cr

・フィボナッチ数列 [fib(0) = 0, fib(1) = 1, fib(n) = fib(n-1) + fib(n-2)]
: fib dup 0= if else dup 1 = if else dup 1- fib swap 2 - fib + endif endif ;
7 fib . cr
: fibi dup 0= if else 0 1 rot 1 for swap over + loop swap drop endif ;
30 fibi . cr
20 0 for I fibi . sp loop cr

・ハノイの塔
: move1 (Move disk ) echo . swap  ( from ) echo . ( to ) echo . cr ;
: hanoi dup 1 = if move1 else
    3 ndup 3 ndup + 6 - minus 4 ndup swap 3 ndup 1- hanoi
    3 ndup 3 ndup 3 ndup move1
    1- rotr swap over + 6 - minus swap rot hanoi
  endif ;
1 2 2 hanoi
1 2 4 hanoi

・N Queens
: N 2 * 3 + ;
: disp dup N ndup 0 for 
    dup N ndup 0 for over I = 
      if (Q ) echo else (. ) echo endif 
    loop cr drop drop 
  loop drop ;
: check over 0 for 
    3 ndup 6 ndup = if drop 0 else 
        3 ndup 6 ndup - abs 3 ndup 6 ndup - = if drop 0 else 1 = endif 
    endif
    5 nrot 5 nrot drop drop 
  loop rotr drop drop ;
: nq dup N ndup 0 for 
    I over 1+ dup N 1- copy 1 check if 
      dup N ndup over 1+ = if dup N copy disp cr else nq endif 
    endif
  drop drop loop ;
: queens dup 0 for I 0 nq drop drop loop drop ;

4 queens

[puzzle というワードを使って上記のハノイの塔と N Queens パズルを解く
ワードをユーザー辞書に追加することができる.]

□ 辞書コマンド
help  システム定義ワード,ユーザー定義ワードの一覧を表示.

forget <name>
<name> という名前を持つワード以降にユーザーが定義したワードを全部消去する.

fgall
ユーザーが定義したワードを全て消去する.

def <name>
<name> という名前を持つワードの定義を,再評価可能な形式で表示する.

□ ワードの探索
ワードはシステム辞書,ユーザー辞書の順に探索される.ユーザー辞書は新し
く登録されたものから検索される.

IV. 変数
□ 変数の定義
var name

仕様
name という変数を作成する.変数は辞書に登録される.見かけ上はユーザー定義
ワードの形態を取る.name を評価すると,ユーザー辞書のインデックスがスタッ
クに保存される.

制限
変数の宣言は実行可能状態でのみ可能.ワードの定義中や,do や for のループの
中では変数の定義は出来ない.

□ 値の呼出し
@

仕様
スタックから辞書のインデックスを取りだし,その番地に含まれる数値をスタッ
クに置く.

□ 値の代入
$

仕様
スタックから辞書のインデックスと数値を取りだし,その番地に登録する.

□ 例題
var x
x @ . cr
10 x $
x @ . cr
: test x @ . cr ;
test