sambaiz-netawsgolangmachinelearninglogkubernetesinfrapythonlambdatensorflowunityfluentdgcpnode.jswebdockerlinuxmysqlelasticsearchjavascripthololensauthc++circleciiosproductreacttypescriptandroidbleeventswiftalgorithmangularcronistioterraformuwp.netcssdatadogfirebasegithubgrpchadoopnorikrarxservervransibleapicdncompresscryptocsharpd3.jsdigdaghugojenkinsjvmkotlinlanguageleveldblispmathneo4jobjective-cpytorchread_paperscikit-learnserializesonnetsparkspecstatisticsvideovscodeTAGsambaiz-netgoyaccでparserを生成しLispのcons,car,cdrの式を評価する(2019-10-15)GoでLispのcons,car,cdrの式を評価したい。scanner)でソースコードを分割しtoken列にして、構文解析器(parser)で構文木を作るなりして評価できるようにする。3)Goの字句解析器と構文解析器Goの字句解析器と構文解析器がGoが実装されているので見てみる。go/scannerソースコードを分割してgo/tokenにする。skipWhitespace()でスペースやタブなどを無視して読み進める(next())。その後、見ている文字(s.ch)によってどう読み進めるかを判断する。trueやbreakといったキーワードなのか、そうでないのか(token.IDENT)を判定する。中身をstringとして読み進める。}.は...かもしれないのでさらにその次の文字まで見ていたりする。token.ELLIPSISgo/parsergo/astを生成する。')'実装を見てみたが再帰の連続でかなり骨が折れた。これを使うことにした。compiler)のGo版。実行すると入力を受け付け、式を評価して表示するが、これはgoyaccの機能ではなく文法ファイルに書いてある挙動。23yacc文法ファイル%%で区切られた3つの部分からなる。定義部%{と%}で囲まれた部分にコードを書けるのでpackage名やimportを書く。<type>で終端記号の種類と型を定義する。大文字と小文字は区別され、非終端記号は小文字で、終端記号は大文字にすることが多いようだ。''で囲まれた一文字記号はここで定義しなくても規則で使える。右辺で右辺が左辺にreduceされる規則を表す。{}の中にはコードが書けて、$$が左辺で$nが右辺のn番目のtokenになる。$2}ユーザ定義部コードを書けてそのまま出力されるが、別のgoファイルに分けた方がエディタのシンタックスハイライトやエラーチェックが効くので書きやすいと思う。intとError(string)を実装したLexerを引数に取る。exprNewParser().Parse(exprlex)}Lex()は値を*<prefix>SymTypeに入れて、一文字記号はそのruneをintにキャストしたものを、それ以外のtoken(NUM)はgoyaccによって数値が割り当てられているのでそれを返す。最後まで読んだら0を返す。NUM}cons,car,cdr評価器を作るということでcons,car,cdr評価器を作る。全体のコードはGitHubにある。sambaiz/goyacc-carcdr-evaluatoryacc文法ファイル文法ファイルのコードは最低限にして同じpackageのgoファイルに書いているのでユーザ定義部は空。$1}}%%Lexerサンプルコードをベースにcons,car,cdr,nilに対応した。errors.New(s)}テストを書く。len(test.outs))})}}Parserサンプルでは直接<prefix>Parse()を呼んでいたが、評価した値を扱えるように<prefix>ParserImplからParseする。