前回からだいぶ間が空いてしまいました。マイコンに載せてチョイとした用途に使うつもりのインタプリタもどきをGtestを活用?して作っていましたが、ソース以外にドキュメント無。自前とは言え仕様は忘却の彼方デス。作業再開にあたってソースを読みながら文法をEBNF化(いい加減だけれども。)後付け、泥縄、いつものことか。
※「IoT何をいまさら」投稿順Indexはこちら
EBNF
Extended Backus-Naur Formは、その名のとおりBNFの拡張記法です。BNFは、文法など定義するのに時々お目にかかるあれです。BNF素人がブツブツ言っていてもショウガない(ノージンジャー)なので、テキトーなドキュメントを引用させていただきます。
普通は、先にBNFなり、EBNFなりで文法を記述してから、それにそってコンパイラなり、インタプリタなり実装するってもんでしょうが、今回は逆です。途中まで「仕掛かかっている」インタプリタもどきのソースを読みながらこれをEBNFに「起こす」ことで記憶をよみがえらせようと。
しかし、ただEBNF化しても面白くないので、1個機能を追加することにいたしました。以下のように let 変数名、その値(式使ってよし)などとしたら値(いまのところ16進整数値のみですが)を変数に格納できるようにしたいと思います。
let X 3+5
まずは、EBNF化した仕様から。最初の1行が追加分。残りは前回までのもの。
ソース変更
まずはヘッダファイル undertest.hppの追加部分。
bool setdU32(uint32_t id, uint32_t val); bool defVU32(char *buf);
関数を2個追加します。IDに対して値を格納するsetdU32と、今回追加のdefvariableの処理実体である defVU32です。
本体undertest.cppのdefVU32の定義が以下に。
//<defvariable> --> let <ident> <exp> bool defVU32(char *buf) { if (strncmp(&buf[currentIndex], "let", 3)) { return false; } currentIndex += 3; nextCh(); uint32_t id; if (toIdU32(buf, &id)) { nextCh(); uint32_t v = expU32(buf); if (errFlag) { return false; } return setdU32(id, v); } return false; }そ
そして値をセットするためのsetdU32が以下に。
bool setdU32(uint32_t id, uint32_t val) { int idx=0; while (idx < MAXID) { if (id == idtable[idx][0]) { idtable[idx][1] = val; return true; } else if (0 == idtable[idx][0]) { idtable[idx][0] = id; idtable[idx][1] = val; return true; } idx++; } return false; }
そして、Google-Testで上記をテストするための testrunner.cpp への追加が以下に。
TEST(testrunner, defVU32) { strcpy(buf, "let X 3+5"); currentIndex = 0; lastIndex = strlen(buf); errFlag = false; errNumber = 0; EXPECT_TRUE(defVU32(buf)); EXPECT_EQ(currentIndex, 9); strcpy(buf, "X"); currentIndex = 0; lastIndex = strlen(buf); errFlag = false; errNumber = 0; EXPECT_EQ(expU32(buf), 3+5); EXPECT_EQ(currentIndex, 1); }
Google-Test結果
まあ、少なくとも
let X 3+5
などと書くと3+5は8で、8を変数Xに代入してくれているみたい。これで変数の書き込み、読み出しはできたので、今度は関数か?