Skip to content

Instantly share code, notes, and snippets.

@gamix255
Last active December 23, 2021 21:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gamix255/0b1eae0fcf538f12bfe5c4de5aa1f116 to your computer and use it in GitHub Desktop.
Save gamix255/0b1eae0fcf538f12bfe5c4de5aa1f116 to your computer and use it in GitHub Desktop.
http://essen.osask.jp/?a21_txt01 のHL言語をbashで実装してみています。

これは何?

Q&A形式でポエム的なものを置いておきます。

Q.何故bash?
A.隙間の時間で実装するために普段、最も使っているshに似ているbashを使ってみます。bashの機能は昨年、短いスクリプトを作る際に趣味で使った程度です。

Q.冗長だったりコーディングスタイルが一定ではないのは何故? A.色々あってという感じですが列挙してみます。

  • 元のHL言語はC言語で言語処理系を実装しています。実装された言語はC言語のサブセットでTiny C的なものです。
    元の処理系の構造を模擬している部分と、bashのネイティブな機能を活かしている部分があるため、スタイルが異なります。
  • 隙間の時間を活用するために編集しずらい環境を使った際にそのままにしている箇所も多くあります。
  • bashのスクリプトの記述になれていないため、後から参照する用途にあえて違う書き方をしている部分も多少あります。

本来もうちょっとなんとかしてから人から見える場所に置こうかとも思いましたが、作者(川合さん)へのお礼までリアクションとしてここにおいてみることにします。(そのため本文よりもむしろこのメモの方が長くなっています。)

Q.何故 HL言語?
A.昨年いくつかのインタプリタに命令を追加したりして遊んでいましたが、もう少し本格的なものに取り組んでみたいと思っていたところ作者(川合秀美さん)のツイート経由で 川合のプログラミング言語自作のためのテキスト第三版#1 ~ 通称「10日くらいでできる!プログラミング言語自作入門」をちらっと見た際に、次のように記載してあった気がします。

  • 少ない行数で書いており、分かりやすさを重視。
  • 仮想マシンをターゲットにしたコンパイラを実装する。
  • その仮想マシンの機械語のインタプリタを実装する。
  • 仮想マシン用の機械語ではなく実在のプロセッサ用のものを生成するのであれば実行時のペナルティは無くなる。

クロスコンパイルすればインタプリタが何であっても速くなる、というのは道理で言語処理系らしきものを隙間時間で少しづつ作ってみるのも面白いのではないかと思いました。

bashで実装してみての所感
普段作るシェルスクリプトはawk混じり、パイプ中心で処理するものが多く、普通の言語のようにシェルスクリプトを使うと、シェルスクリプトが嫌いになったり苦手意識を持つ人がいるのがよくわかります。思った以上に進捗が悪いけれどもう少し進めてみます。

今後の展開
今のところ元の処理系の動きに近い感じの文法を処理できている気がしますが、続けていてもそのうち脱線していくかもしれません。

#!/bin/bash
if [ $# -lt 1 ]; then
echo "usage> " $0 "program-file"
exit 1
fi
TEXT=`cat $1` #デミリタは全て1文字の空白に変換
declare -A hl_var=( [1]=dummy )
for i in `seq 0 9`
do
hl_var[$i]=$i
done
TEXT_LENGTH=${#TEXT}
for (( PC=0 ; PC < TEXT_LENGTH ; PC++ ))
do
if [[ `echo ${TEXT:PC:1}|wc -c` == 1 || ${TEXT:PC:1} == ';' || ${TEXT:PC:1} == '\t' ]] ; then
continue
elif [[ ${TEXT:PC+1:1} == "=" && ${TEXT:PC+3:1} == ';' ]] ; then
hl_var[${TEXT:PC:1}]=${hl_var[${TEXT:PC+2:1}]}
elif [[ ${TEXT:PC+1:1} == "=" && ${TEXT:PC+3:1} == '+' && ${TEXT:PC+5:1} == ';' ]] ; then
hl_var[${TEXT:PC:1}]=$(( hl_var[${TEXT:PC+2:1}] + hl_var[${TEXT:PC+4:1}] ))
elif [[ ${TEXT:PC+1:1} == "=" && ${TEXT:PC+3:1} == '-' && ${TEXT:PC+5:1} == ';' ]] ; then
hl_var[${TEXT:PC:1}]=$(( hl_var[${TEXT:PC+2:1}] - hl_var[${TEXT:PC+4:1}] ))
elif [[ ${TEXT:PC:1} == "p" && ${TEXT:PC+1:1} == 'r' && ${TEXT:PC+5:1} == ' ' && \
${TEXT:PC+7:1} == ';' ]] ; then
echo ${hl_var[${TEXT:PC+6:1}]}
else
echo syntax error $PC ${TEXT:PC:1}
exit
fi
done
set | grep _var
#!/bin/bash
if [ $# -lt 1 ]; then
echo "usage> " $0 "program-file"
exit 1
fi
declare -a tl
declare -a tc
MAX_TC=1000
tcs=0
tcb=0
TEXT=`cat $1` #デミリタは全て1文字の空白に変換
declare -A hl_var=( [1]=dummy )
ret_getTc=0
function getTc () {
local len=$2
local str="${TEXT:$1:$len}"
local i
for (( i=0 ; i < tcs ; i++ ))
do
if [[ $len = "${tl[$i]}" && $str = "${TEXT:ts[$i]:len}" ]];then
break;
fi
done
if [[ $i == $tcs ]]; then
if [[ $tcs -gt $MAX_TC ]]; then
echo "too many tokens"
exit 1
fi
ts[$i]=$1
tl[$i]="$len"
let tcs++
hl_var[$i]=0
[[ $str =~ ^[0-9]*$ ]] && hl_var[$i]=$str
fi
ret_getTc=$i
}
ret_lexer=0
function lexer () {
local str="$1"
local len=0
local i
local j=0
local sp_char="[=+-/!%&~|<>?:.#\*]"
local sp_single_char="[(){}[\];,]"
local AlphaNum="[A-Z|a-z|0-9]"
local space=" "
str_length=${#str}
for (( i=0 ; ; ))
do
if [[ $i -ge $str_length ]] ;then
ret_lexer=$j
return
else
len=0
case "${str:$i:1}" in
$space )
let i++
continue
;;
$sp_char )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$sp_char )
let len++
let k++
;;
* )
break
;;
esac
done
;;
$sp_single_char )
let len++
;;
$AlphaNum )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$AlphaNum )
let len++
let k++
;;
* )
break
;;
esac
done
;;
* )
local other="${str:$i:1}"
if [[ 0 = `echo -n ${other}|wc -c` ]];then
let i++
continue
fi
;;
esac
fi
getTc $i $len $j
tc["$j"]=$ret_getTc
let i+=len
let j++
done
}
function test_lexer () {
TEXT=`cat $1`
lexer "$TEXT"
set | grep ^tc
set | grep ^tcs
set | grep ^ts=
set | grep ^hl_var=
}
readonly f=1
readonly t=0
function isAlphabetOrNumber () {
# echo is alpha num check is $1
[[ $1 =~ ^[0-9] ]] && return $t
[[ $1 =~ ^[a-z] ]] && return $t
[[ $1 =~ ^[A-Z] ]] && return $t
[[ $1 =~ ^_ ]] && return $t
return $f
}
function test_isAlphaNum () {
if isAlphabetOrNumber $2
then
echo ok
else
echo ng
fi
exit
}
function main () {
TEXT_LENGTH=${#TEXT}
local PC0
local PC1
lexer "$TEXT"
PC1=$ret_lexer
LAST=$TEXT_LENGTH
TEXT+="."
getTc $LAST 1; let LAST++
tc[$PC1]=$ret_getTc
tc[$((PC1+1))]=$ret_getTc
tc[$((PC1+2))]=$ret_getTc
tc[$((PC1+3))]=$ret_getTc
TEXT+=";"; getTc $LAST 1 ; let LAST++
local TKN_semi=$ret_getTc
TEXT+="="; getTc $LAST 1 ; let LAST++
local TKN_eql=$ret_getTc
TEXT+="+"; getTc $LAST 1 ; let LAST++
local TKN_plus=$ret_getTc
TEXT+="-"; getTc $LAST 1 ; let LAST++
local TKN_minus=$ret_getTc
TEXT+="print"; getTc $LAST 5 ; let LAST+=5
local TKN_print=$ret_getTc
for (( PC=0 ; PC < PC1 ; PC++ ))
do
if [[ ${tc[$PC + 1]} == $TKN_eql && ${tc[$PC + 3]} == $TKN_semi ]]; then
hl_var[${tc[PC]}]=${hl_var[${tc[PC+2]}]}
elif [[ ${tc[$PC + 1]} == $TKN_eql && ${tc[$PC + 3]} == $TKN_plus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] + hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC + 1]} == $TKN_eql && ${tc[$PC + 3]} == $TKN_minus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] - hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC]} == $TKN_print && ${tc[$PC + 2]} == $TKN_semi ]] ; then
echo ${hl_var[${tc[$PC + 1]}]}
fi
for ((; ${tc[$PC]} != $TKN_semi && $PC < $PC1; ))
do
let PC++
done
done
}
main
set | grep ^tc=
set | grep ^tcs
set | grep ^ts=
set | grep ^hl_var=
for ((i = 0; i < ${#ts[@]}; i++)) {
echo Token $i "${TEXT:${ts[$i]}:${tl[$i]}}"
}
exit
#!/bin/bash
if [ $# -lt 1 ]; then
echo "usage> " $0 "program-file"
exit 1
fi
declare -a tl
declare -a tc
MAX_TC=1000
tcs=0
tcb=0
TEXT=`cat $1` #デミリタは全て1文字の空白に変換
declare -A hl_var=( [1]=dummy )
ret_getTc=0
function getTc () {
local len=$2
local str="${TEXT:$1:$len}"
local i
for (( i=0 ; i < tcs ; i++ ))
do
if [[ $len = "${tl[$i]}" && $str = "${TEXT:ts[$i]:len}" ]];then
break;
fi
done
if [[ $i == $tcs ]]; then
if [[ $tcs -gt $MAX_TC ]]; then
echo "too many tokens"
exit 1
fi
ts[$i]=$1
tl[$i]="$len"
let tcs++
hl_var[$i]=0
[[ $str =~ ^[0-9]*$ ]] && hl_var[$i]=$str
fi
ret_getTc=$i
}
ret_lexer=0
function lexer () {
local str="$1"
local len=0
local i
local j=0
local sp_char="[=+-/!%&~|<>?:.#\*]"
local sp_single_char="[(){}[\];,]"
local AlphaNum="[A-Z|a-z|0-9]"
local space=" "
str_length=${#str}
for (( i=0 ; ; ))
do
if [[ $i -ge $str_length ]] ;then
ret_lexer=$j
return
else
len=0
case "${str:$i:1}" in
$space )
let i++
continue
;;
$sp_char )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$sp_char )
let len++
let k++
;;
* )
break
;;
esac
done
;;
$sp_single_char )
let len++
;;
$AlphaNum )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$AlphaNum )
let len++
let k++
;;
* )
break
;;
esac
done
;;
* )
local other="${str:$i:1}"
if [[ 0 = `echo -n ${other}|wc -c` ]];then
let i++
continue
else
echo syntax error on lexer
exit 1
fi
;;
esac
fi
getTc $i $len $j
tc["$j"]=$ret_getTc
let i+=len
let j++
done
}
function main () {
TEXT_LENGTH=${#TEXT}
local PC0
local PC1
lexer "$TEXT"
PC1=$ret_lexer
LAST=$TEXT_LENGTH
TEXT+="."
getTc $LAST 1; let LAST++
tc[$PC1]=$ret_getTc
tc[$((PC1+1))]=$ret_getTc
tc[$((PC1+2))]=$ret_getTc
tc[$((PC1+3))]=$ret_getTc
TEXT+=";"; getTc $LAST 1 ; let LAST++
local TKN_semi=$ret_getTc
TEXT+="="; getTc $LAST 1 ; let LAST++
local TKN_assign=$ret_getTc
TEXT+="+"; getTc $LAST 1 ; let LAST++
local TKN_plus=$ret_getTc
TEXT+="-"; getTc $LAST 1 ; let LAST++
local TKN_minus=$ret_getTc
TEXT+=":"; getTc $LAST 1 ; let LAST++
local TKN_colon=$ret_getTc
TEXT+="("; getTc $LAST 1 ; let LAST++
local TKN_Lparen=$ret_getTc
TEXT+=")"; getTc $LAST 1 ; let LAST++
local TKN_Rparen=$ret_getTc
TEXT+="print"; getTc $LAST 5 ; let LAST+=5
local TKN_print=$ret_getTc
TEXT+="goto"; getTc $LAST 4 ; let LAST+=4
local TKN_goto=$ret_getTc
TEXT+="if"; getTc $LAST 2 ; let LAST+=2
local TKN_if=$ret_getTc
TEXT+="=="; getTc $LAST 2 ; let LAST+=2
local TKN_cmp_eq=$ret_getTc
TEXT+="!="; getTc $LAST 2 ; let LAST+=2
local TKN_cmp_neq=$ret_getTc
TEXT+="<"; getTc $LAST 1 ; let LAST++
local TKN_cmp_lt=$ret_getTc
for (( PC=0 ; PC < PC1 ; PC++ ))
do
if [[ ${tc[$PC + 1]} == $TKN_colon ]]; then
hl_var[${tc[PC]}]=$(( PC + 2))
fi
done
# local debug_c=0
for (( PC=0 ; PC < PC1 ; ))
do
# let debug_c++
# echo -n dc:$debug_c pc:$PC
# for (( j=PC ; ${tc[$j]} != $TKN_semi && $j < $PC1; j++ ))
# do echo -n " ${TEXT:${ts[${tc[j]}]}:${tl[${tc[j]}]}}"
# done
# echo ${TEXT:${ts[${tc[j]}]}:${tl[${tc[j]}]}}
if [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_semi ]]; then
hl_var[${tc[PC]}]=${hl_var[${tc[PC+2]}]}
elif [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_plus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] + hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_minus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] - hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC]} == $TKN_print && ${tc[$PC + 2]} == $TKN_semi ]] ; then
echo ${hl_var[${tc[$PC + 1]}]}
elif [[ ${tc[$PC + 1]} == $TKN_colon ]]; then
let PC+=2
continue
elif [[ ${tc[$PC]} == $TKN_goto && ${tc[$PC + 2]} == $TKN_semi ]]; then
PC=${hl_var[${tc[PC+1]}]}
continue
elif [[ ${tc[$PC]} == $TKN_if && \
${tc[$PC + 1]} == $TKN_Lparen && \
${tc[$PC + 5]} == $TKN_Rparen && \
${tc[$PC + 6]} == $TKN_goto && \
${tc[$PC + 8]} == $TKN_semi ]]; then
local GPC=${hl_var[${tc[PC+7]}]}
local v0=${hl_var[${tc[PC+2]}]}
local v1=${hl_var[${tc[PC+4]}]}
if [[ ${tc[$PC + 3]} == $TKN_cmp_neq && v0 != v1 ]]; then
PC=$GPC
continue
elif [[ ${tc[$PC + 3]} == $TKN_cmp_eq && v0 == v1 ]]; then
PC=$GPC
continue
elif [[ ${tc[$PC + 3]} == $TKN_cmp_lt && v0 -lt v1 ]]; then
PC=$GPC
continue
fi
else
echo syntax error on main
exit 1
fi
for ((; ${tc[$PC]} != $TKN_semi && $PC < $PC1; ))
do
let PC++
done
let PC++
done
}
main
set | grep ^tc=
set | grep ^tcs
set | grep ^ts=
set | grep ^hl_var=
for ((i = 0; i < ${#ts[@]}; i++)) {
echo Token $i "${TEXT:${ts[$i]}:${tl[$i]}}"
}
exit
#!/bin/bash
declare -A ts
declare -a rts
declare -a tc
MAX_TC=1000
tcs=0
tcb=0
declare -A hl_var=( [1]=dummy )
ret_getTc=0
function getTc () {
local str=$1
local i
if [[ -n "${ts[$str]}" ]]; then
i=${ts[$str]}
else
i=${#ts[@]}
ts[$str]=$i
rts[$i]=$str
hl_var[$i]=0
[[ $str =~ ^[0-9]*$ ]] && hl_var[$i]=$str
fi
if [[ $i -gt $MAX_TC ]]; then
echo "too many tokens"
exit 1
fi
ret_getTc=$i
}
ret_lexer=0
function lexer () {
local str="$1"
local len=0
local i
local j=0
local sp_char="[=+-/!%&~|<>?:.#\*]"
local sp_single_char="[(){}[\];,]"
local AlphaNum="[A-Z|a-z|0-9]"
local space=" "
str_length=${#str}
for (( i=0 ; ; ))
do
if [[ $i -ge $str_length ]] ;then
ret_lexer=$j
return
else
len=0
case "${str:$i:1}" in
$space )
let i++
continue
;;
$sp_char )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$sp_char )
let len++
let k++
;;
* )
break
;;
esac
done
;;
$sp_single_char )
let len++
;;
$AlphaNum )
for (( k=i ; ; ))
do
case "${str:$k:1}" in
$AlphaNum )
let len++
let k++
;;
* )
break
;;
esac
done
;;
* )
local other="${str:$i:1}"
if [[ 0 = `echo -n ${other}|wc -c` ]];then
let i++
continue
else
echo syntax error on lexer
exit 1
fi
;;
esac
fi
getTc "${str:$i:$len}"
tc["$j"]=$ret_getTc
let i+=len
let j++
done
}
function run () {
TEXT_LENGTH=${#TEXT}
local PC0
local PC1
lexer "$TEXT"
PC1=$ret_lexer
getTc "."
tc[$PC1]=$ret_getTc
tc[$((PC1+1))]=$ret_getTc
tc[$((PC1+2))]=$ret_getTc
tc[$((PC1+3))]=$ret_getTc
getTc ";"
local TKN_semi=$ret_getTc
getTc "="
local TKN_assign=$ret_getTc
getTc "+"
local TKN_plus=$ret_getTc
getTc "-"
local TKN_minus=$ret_getTc
getTc ":"
local TKN_colon=$ret_getTc
getTc "("
local TKN_Lparen=$ret_getTc
getTc ")"
local TKN_Rparen=$ret_getTc
getTc "print"
local TKN_print=$ret_getTc
getTc "goto"
local TKN_goto=$ret_getTc
getTc "if"
local TKN_if=$ret_getTc
getTc "=="
local TKN_cmp_eq=$ret_getTc
getTc "!="
local TKN_cmp_neq=$ret_getTc
getTc "<"
local TKN_cmp_lt=$ret_getTc
for (( PC=0 ; PC < PC1 ; PC++ ))
do
if [[ ${tc[$PC + 1]} == $TKN_colon ]]; then
hl_var[${tc[PC]}]=$(( PC + 2))
fi
done
for (( PC=0 ; PC < PC1 ; ))
do
if [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_semi ]]; then
hl_var[${tc[PC]}]=${hl_var[${tc[PC+2]}]}
elif [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_plus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] + hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC + 1]} == $TKN_assign && ${tc[$PC + 3]} == $TKN_minus && ${tc[$PC + 5]} == $TKN_semi ]] ; then
hl_var[${tc[$PC]}]=$(( hl_var[${tc[$PC + 2]}] - hl_var[${tc[$PC + 4]}] ))
elif [[ ${tc[$PC]} == $TKN_print && ${tc[$PC + 2]} == $TKN_semi ]] ; then
echo ${hl_var[${tc[$PC + 1]}]}
elif [[ ${tc[$PC + 1]} == $TKN_colon ]]; then
let PC+=2
continue
elif [[ ${tc[$PC]} == $TKN_goto && ${tc[$PC + 2]} == $TKN_semi ]]; then
PC=${hl_var[${tc[PC+1]}]}
continue
elif [[ ${tc[$PC]} == $TKN_if && \
${tc[$PC + 1]} == $TKN_Lparen && \
${tc[$PC + 5]} == $TKN_Rparen && \
${tc[$PC + 6]} == $TKN_goto && \
${tc[$PC + 8]} == $TKN_semi ]]; then
local GPC=${hl_var[${tc[PC+7]}]}
local v0=${hl_var[${tc[PC+2]}]}
local v1=${hl_var[${tc[PC+4]}]}
if [[ ${tc[$PC + 3]} == $TKN_cmp_neq && v0 != v1 ]]; then
PC=$GPC
continue
elif [[ ${tc[$PC + 3]} == $TKN_cmp_eq && v0 == v1 ]]; then
PC=$GPC
continue
elif [[ ${tc[$PC + 3]} == $TKN_cmp_lt && v0 -lt v1 ]]; then
PC=$GPC
continue
fi
else
echo syntax error on main
exit 1
fi
for ((; ${tc[$PC]} != $TKN_semi && $PC < $PC1; ))
do
let PC++
done
let PC++
done
}
if [ $# -eq 1 ]; then
TEXT=`cat $1` #デミリタは全て1文字の空白に変換
run
else
for (( ; ; ))
do
read -p "> " INPUT_STR
case $INPUT_STR in
cat* )
cat ${INPUT_STR:4}
;;
run* )
TEXT=`cat ${INPUT_STR:4} `
run
;;
exit )
break;
;;
* )
TEXT=$INPUT_STR
run
;;
esac
done
fi
set | grep ^tc=
set | grep ^ts=
set | grep ^hl_var=
for ((i = 0; i < ${#ts[@]}; i++)) {
echo Token $i "${rts[$i]}"
}
exit

HL-1:

  • C言語の文字列・ポインタ・配列モデルと異なる言語を使うためどうするか迷ったけれど、単一の変数TEXTからの変数展開でメモリアクセスの代わりとするようにしてみた。変数は普通に連想配列。
  • 上記のメモリを文字列で実現するモデルのほかに配列にするモデルも想定したけれど吉と出るか凶と出るか。
  • bashも変数展開もあまり使わないので新鮮。プログラム読み込みの部分など手抜きで実装

HL-2:

  • 早速HL-1の実装が裏目に出て、元プログラムのmain関数に該当するところを大幅に書き換えることに。
  • functionの戻り値に自由が無いのでGlobal変数経由で返すことに。構造化されていないBASICを思い出す。ここも普段のスタイルとの違いがでてきた。シェルスクリプトでは、パイプでつなぐか、サブシェルの出力を取り込む形にすることが多いけれどGlobal変数との相性がよくない。
  • プログラム本文の後ろに文字列を付け足して特定のTokenのIDを取得。
  • lexer、できているのかできていないのか。あまりテストしていないけれども。

HL-3:

  • 代入はイコールじゃなくてアサインにToken名称を変更した。
  • しばらくうまく動かせずに試行錯誤していると少しコツがつかめてきて、HL-2のバグに気が付いて修正してみたり。
  • timeはシェル起動時にtimeコマンドで代用するので実装しないことに。

HL-4:

  • 一回の起動中にlexerを複数呼ぶことが出てくるためToken取得のgetTcとのインターフェースを変更し、普通に連想配列に入れるモデルに変更する。
  • 上記の変更を行うと残りはあっさりと実現できた。
  • 行末のセミコロンを付与する部分はlexerが今でもexit 1するままだし、あんまり気が進まず実装していない。その代わり、ファイル表示のcatを入れてみる。
  • repl、楽しいかなと思っていたけれど、replは楽しかった。

HL-5:(未実装)

  • HL-4ではbashに寄せてシンプルになったのでフレーズ単位のマッチ、そして、ワイルカード部分の取り出しをどうするのか考えている。grep、bashの正規表現、awkのパターンマッチなど複数のやり方がありそうに思える。自分の習熟も考えるとbashという選択はどうだったのか。と思わなくもない。
  • 内部処理用パターンマッチの機能とDSLの実装しているとも言い換えることができると思う。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment