【その2・Nim】Nimで「Goでつくるインタプリタ」を写経する 。
前回
1~4章を終えて
私は一応エンジニアとして働かせていただいているのですが、
経験上、BNF法を応用したりとかでオモチャやメタ言語を生成することもあるかなと思います。。。。。。。。(んなことないか)
本書を読んだからといってプログラミング言語のデザインについて完璧になれるわけでもないかなと思いますが、
プログラミング言語自体が抱える問題と戦いたい
インタプリタ言語の処理工程ついて知見を深めたい
TDDの快感を知りたい
などの人は是非一度手にとって欲しい本です!
ざっとこんな感じ
1+2
>> 1 + 2
[DEBUG]: parseProgram curToken: (typ: INT, literal: "1")
[DEBUG]: BEGIN parseExpressionStatement
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIntegerliteral
[DEBUG]: END parseIntegerliteral: 1
[DEBUG]: BEGIN parseInfixExpression
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIntegerliteral
[DEBUG]: END parseIntegerliteral: 2
[DEBUG]: END parseExpression: some(2)
[DEBUG]: END parseInfixExpression: (1 + 2)
[DEBUG]: END parseExpression: some((1 + 2))
[DEBUG]: END parseExpressionStatement: some((1 + 2))
[DEBUG]: add statements: (1 + 2)
[DEBUG]: BEGIN: is ast.Program
[DEBUG]: BEGIN: (1 + 2) is ast.ExpressionStatement
[DEBUG]: BEGIN: (1 + 2) is ast.InfixExpression
[DEBUG]: BEGIN: 1 is ast.IntegerLiteral
[DEBUG]: DONE: ast.IntegerLiteral: 1
[DEBUG]: BEGIN: 2 is ast.IntegerLiteral
[DEBUG]: DONE: ast.IntegerLiteral: 2
[DEBUG]: DONE: ast.InfixExpression: 3
[DEBUG]: DONE: ast.ExpressionStatement: 3
[DEBUG]: DONE: ast.Program: 3
3
let a = fn(x) { x * x * x }
>> let a = fn(x) { x * x * x }
[DEBUG]: parseProgram curToken: (typ: LET, literal: "let")
[DEBUG]: BEGIN parseLetStatement
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseFunctionLiteral
[DEBUG]: BEGIN parseFunctionParameters
[DEBUG]: END parseFunctionParameters: @[some(x)]
[DEBUG]: BEGIN parseBlockStatement
[DEBUG]: BEGIN parseExpressionStatement
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIdentifier
[DEBUG]: END parseIdentifier: x
[DEBUG]: BEGIN parseInfixExpression
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIdentifier
[DEBUG]: END parseIdentifier: x
[DEBUG]: END parseExpression: some(x)
[DEBUG]: END parseInfixExpression: (x * x)
[DEBUG]: BEGIN parseInfixExpression
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIdentifier
[DEBUG]: END parseIdentifier: x
[DEBUG]: END parseExpression: some(x)
[DEBUG]: END parseInfixExpression: ((x * x) * x)
[DEBUG]: END parseExpression: some(((x * x) * x))
[DEBUG]: END parseExpressionStatement: some(((x * x) * x))
[DEBUG]: END parseBlockStatement: some(((x * x) * x))
[DEBUG]: END parseFunctionLiteral: some(fn)
[DEBUG]: END parseExpression: some(fn)
[DEBUG]: END parseLetStatement: some(let a = fn;)
[DEBUG]: add statements: let a = fn;
[DEBUG]: BEGIN: is ast.Program
[DEBUG]: BEGIN: let a = fn; is ast.LetStatement
[DEBUG]: BEGIN: fn is ast.FunctionLiteral
[DEBUG]: DONE: ast.FunctionLiteral: Object
[DEBUG]: DONE: ast.LetStatement: Object
[DEBUG]: DONE: ast.Program: Object
>> a(10)
[DEBUG]: parseProgram curToken: (typ: IDENT, literal: "a")
[DEBUG]: BEGIN parseExpressionStatement
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIdentifier
[DEBUG]: END parseIdentifier: a
[DEBUG]: BEGIN parseCallExpression
[DEBUG]: BEGIN parseExpressionList
[DEBUG]: BEGIN parseExpression
[DEBUG]: BEGIN parseIntegerliteral
[DEBUG]: END parseIntegerliteral: 10
[DEBUG]: END parseExpression: some(10)
[DEBUG]: END parseExpressionList: @[10]
[DEBUG]: END parseCallExpression: a(10)
[DEBUG]: END parseExpression: some(a(10))
[DEBUG]: END parseExpressionStatement: some(a(10))
[DEBUG]: add statements: a(10)
[DEBUG]: BEGIN: is ast.Program
[DEBUG]: BEGIN: a(10) is ast.ExpressionStatement
[DEBUG]: BEGIN: a(10) is ast.CallExpression
[DEBUG]: BEGIN: a is ast.Identifier
[DEBUG]: DONE: ast.Identifier: Object
[DEBUG]: BEGIN: 10 is ast.IntegerLiteral
[DEBUG]: DONE: ast.IntegerLiteral: 10
[DEBUG]: BEGIN: { is ast.BlockStatement
[DEBUG]: BEGIN: ((x * x) * x) is ast.ExpressionStatement
[DEBUG]: BEGIN: ((x * x) * x) is ast.InfixExpression
[DEBUG]: BEGIN: (x * x) is ast.InfixExpression
[DEBUG]: BEGIN: x is ast.Identifier
[DEBUG]: DONE: ast.Identifier: 10
[DEBUG]: BEGIN: x is ast.Identifier
[DEBUG]: DONE: ast.Identifier: 10
[DEBUG]: DONE: ast.InfixExpression: 100
[DEBUG]: BEGIN: x is ast.Identifier
[DEBUG]: DONE: ast.Identifier: 10
[DEBUG]: DONE: ast.InfixExpression: 1000
[DEBUG]: DONE: ast.ExpressionStatement: 1000
[DEBUG]: DONE: ast.BlockStatement: 1000
[DEBUG]: DONE: ast.CallExpression: 1000
[DEBUG]: DONE: ast.ExpressionStatement: 1000
[DEBUG]: DONE: ast.Program: 1000
1000
Nimで実装した内容についてまとめる
nimで本書を写経した際に少しつまづいた点を振り返る
Interfaceが無いのがクセェ
今回はinterfaceではなくtupleで代用しました。。。。。。。。
type IParser* = tuple
parseProgram : proc(): Option[ast.Program]
errors: proc(): seq[ast.AstException]
proc newParser*(lexer: lexer.ILexer): IParser =
...
return (
parseProgram: proc(): Option[ast.Program] = parser.parseProgram(),
errors: proc(): seq[ast.AstException] = parser.errs
);
proc, func, methodとかいうのがありすぎ
ここら辺ですね。。。。👇
一生綺麗に書ける自信がないですw(全てprocなら逆に綺麗なのでは???????)
型の指定が多すぎて調べるのが大変ですわ
毎回、見返してもなんのために書いたか覚えていないので、
すごい細かく型を書かされます。。。。。
無知な僕にはハードルが高かったです。
{.noSideEffect, gcsafe, locks: 0.}
{.pure.}
{.base.}
成果物
まとめ
今回は、nimの勉強が主でした。。。。
Pythonのように簡単に書けるっていう風にはなりませんでした。。
Nim難しいです。文法は確かに簡単です(Pythonライクなので)
Golangで書かれている本はすごい読みやすいのではないかと思うぐらい
Golangは簡単なのでおすすめです!
(個人的には書いていて面白くありません。。。。気のせいか)
SP
for文なんて実装してないですわ。
関数が再帰的に処理できる時点でfor文なんて必要ないですよね?
この次はデータサイエンス系のオライリー本漁って
実装して記事にでもしようと思うのですが、
言語を何にしようか迷い中です!….
PSのPS
ワイにもできる副業がないものかと
嘆いているが、何もできないんだろうな。。。
ゲームでもするかぁ。。。。