ERROR: syntax error, unexpected VAR, expecting $end を追う
きっかけ
次のコードを選択して実行するとエラーになる。
コード1
var aa; aa = 1; var bb; bb = 1 + aa;
エラー
ERROR: syntax error, unexpected VAR, expecting $end in file 'selected text' line 3 char 3: var bb; ^^^ bb = 1 + aa; ----------------------------------- ERROR: Command line parse failed -> nil
"シンタックスエラーなり。予期せぬVARがある。そこは終端記号があるべき。場所は4行目云々。"と読める。
一方、こちらの場合は問題なく通る。
コード2
var aa; var bb; aa = 1; bb = 1 + aa;
出力結果
-> 2
どうやら、var
宣言した後に「式」を書いて、その後にまたvar
宣言するとエラーになるらしい。斯くして、var
宣言はまとめて書いた後に「式」を書けば良いという教訓を得た。
のは良いが、どうせならということでSuperColliderのソースを追ってどの辺りでエラーになったのかを確かめてみよう。
コードを追う
まずは検索
githubのsupercolliderの中をsyntax error, unexpected
で検索する。すると、lang/LangSource/Bison/lang11d_tab.cppあたりで引っかかっているのを発見。
このファイルはどのフォルダに入っているのかを見ると、
supercollider/lang/LangSource/Bison/ lang11d lang11d_tab.cpp lang11d_tab.h make_parser.sh
となっている。make_parser.shというシェルスクリプトで何やら生成しているのだろう。また「make parser」、ということはパーサー(構文解析器)をメイク(作る)に違いない。このファイルを見ると、
#!/bin/sh bison -o lang11d_tab.cpp lang11d
と記述してある。意味的には「bisonを実行、出力ファイルはlang11d_tab.cppで入力ファイルはlang11d」であろう。ということなので、肝となるのは入力ファイルのlang11dに違いない。
bisonとは?
bisonとはWikipediaによると、
Bison(バイソン)とは構文解析器を生成するパーサジェネレータの一種であり、CコンパイラとしてのGCCのサポートのために開発されたフリーソフトウェアである。
とのこと。このことから、lang11dは構文解析機を生成するための入力ファイルであることが分かる。つまりSuperColliderのsyntaxはここで定義されているのだろう、と認識してみることにする。
該当箇所を追う
以下、lang11dから該当しそうな箇所を引用しながらコードを追う。
VARを探す
class宣言の方にもVARが見つかったけれど、そちらではなく以下の定義が怪しい。怪しいというのは、普段使っているVARの定義っぽいということである。
funcvardecl : VAR vardeflist ';' { $$ = (intptr_t)newPyrVarListNode((PyrVarDefNode*)$2, varLocal); } ;
これは、「funcvardeclとは、VARというtokenに、vardeflistと';'が続くものである」と読める。「VARに続く次の文字列はvardeflistである」とも読める。では、vardeflistは?というと、
vardeflist : vardef | vardeflist ',' vardef { $$ = (intptr_t)linkNextNode((PyrParseNode*)$1, (PyrParseNode*)$3); } ;
「vardeflistとは、vardefもしくはvardeflistに','とvardefと表されるものである」と読める。再帰的な定義である。vardefにカンマを打ちvardefを更に書いてもvardeflistと認める、ということだろうか?
では、vardefとはなんぞや?と探してみると、
vardef : name { $$ = (intptr_t)newPyrVarDefNode((PyrSlotNode*)$1, NULL, 0); } | name '=' expr { $$ = (intptr_t)newPyrVarDefNode((PyrSlotNode*)$1, (PyrParseNode*)$3, 0); } | name '(' exprseq ')' { PyrParseNode* node = (PyrParseNode*)$3; node->mParens = 1; $$ = (intptr_t)newPyrVarDefNode((PyrSlotNode*)$1, node, 0); } ;
「vardefとは、nameまたはname = exprまたはname(expreseq)である」とのこと。ここで、nameは
%token NAME INTEGER SC_FLOAT ACCIDENTAL SYMBOL STRING ASCII PRIMITIVENAME CLASSNAME CURRYARG
にあるようにtoken(終端記号)である。exprとexprseqはいわゆる式である。長いので引用は割愛する。
ひとまずまとめると、lang11dから普段使うVARのようなものを調べた結果、VAR v1(,v2,v3,...,vn);のうち、v1(,v2,v3,...,vn)の部分をfuncvardeclと呼ぶことが分かった。次にfuncvardeclを見てみる
funcvardeclを探す
funcvardeclのすぐ上に幾つか見つかる。
funcvardecls : { $$ = 0; } | funcvardecls funcvardecl { $$ = (intptr_t)linkNextNode((PyrParseNode*)$1, (PyrParseNode*)$2); } ; funcvardecls1 : funcvardecl | funcvardecls1 funcvardecl { $$ = (intptr_t)linkNextNode((PyrParseNode*)$1, (PyrParseNode*)$2); } ; funcvardecl : VAR vardeflist ';' { $$ = (intptr_t)newPyrVarListNode((PyrVarDefNode*)$2, varLocal); }
funcvardeclを並べたものをfuncvardecls・funcvardecls1と呼ぶ。では、funcvardeclsは・・・と更に辿ると面白い箇所を発見した。
cmdlinecode : '(' funcvardecls1 funcbody ')' { $$ = (intptr_t)newPyrBlockNode(NULL, (PyrVarListNode*)$2, (PyrParseNode*)$3, false); } | funcvardecls1 funcbody { $$ = (intptr_t)newPyrBlockNode(NULL, (PyrVarListNode*)$1, (PyrParseNode*)$2, false); } | funcbody { $$ = (intptr_t)newPyrBlockNode(NULL, NULL, (PyrParseNode*)$1, false); } ;
cmdlinecodeの定義である。中括弧内を省略して書くと、
cmdlinecode : '(' funcvardecls1 funcbody ')' | funcvardecls1 funcbody | funcbody ;
となる。cmdlinecodeは察するにコマンドラインコードであろう。IDEにコードを書いて選択して実行することを指すものだと思われる。そして、その定義というか受理される文法は
- '(' funcvardecls1 funcbody ')'
- funcvardecls1 funcbody
- funcbody
の3タイプしかない。念のためfuncbody(おそらく関数本体を意味する)を見ると、
funcbody : funretval | exprseq funretval { $$ = (intptr_t)newPyrDropNode((PyrParseNode*)$1, (PyrParseNode*)$2); } ;
funretval(おそらく関数戻り値を意味する)を見ると、
funretval : { $$ = (intptr_t)newPyrBlockReturnNode(); } | '^' expr optsemi { $$ = (intptr_t)newPyrReturnNode((PyrParseNode*)$2); } ;
となっている。つまり、funcbodyにVARは含まれていないことが分かる。
まとめ
VAR宣言はfuncbodyより前に書かれていないとcmdlinecodeとしては受理されず、よってsyntax error扱いされると思われる。斯くして、var宣言はまとめて書いた後に「式」を書けば良いという教訓は妥当であると言えよう。