一度にマクロまで拡張すると修正が多そうなので、一旦、数式と値にEBNFを分割し、数式のEBNFから値のEBNFの(非)終端記号を参照できる様な仕組み(imports,exports)を作ってみた。
(*$ SEQUENCE SEPARATOR IS NOT REQUIRED $*)
(*$ export {value_cell,time_stamp,date,number,boolean,sqstring as singleQuoteString,text} from "CELL_VALUE" $*)
(* セルのデータ *)
value_cell = time_stamp | date | time | number | boolean | sqstring | text ;
time_stamp = date ' ' time ;
date = yyyy '/' MM '/' dd | MM '/' dd ;
yyyy = /\\d{4}/ ;
MM = /\\d{1,2}/ ;
dd = /\\d{1,2}/ ;
time = HH ':' mm ':' ss | HH ':' mm ;
HH = /\\d{1,2}/ ;
mm = /\\d{1,2}/ ;
ss = /\\d{1,2}/ ;
number = /[-]?([0-9]+)([.][0-9]*)?/ ;
boolean = /true/i | /false/i ;
sqstring = /'.*/ ;
text = /.+/ ;
(*$ SEQUENCE SEPARATOR IS NOT REQUIRED $*)
(*$ export expr from "EXPR" $*)
(*$ import defaultExport from "CELL_VALUE" $*)
(* セルのデータ *)
cell = expr_cell | value_cell ;
(* 数式 *)
expr_cell = '=' expr ;
expr = logical_expr ;
logical_expr = add_sub_expr [ ( '=' | '>' | '<' | '>=' | '<=' | '<>' ) add_sub_expr ] ;
add_sub_expr = mul_div_expr { ( '+' | '-' | '&' ) mul_div_expr } ;
mul_div_expr = factor { ( '*' | '/' ) factor } ;
factor = value | parenthesis_expr ;
parenthesis_expr = '(' expr ')' ;
value = number | boolean | function_def | a1_range | a1 | dqstring ;
a1_range = a1 ':' a1 ;
a1 = /([A-Za-z]+[1-9][0-9]*)/ ;
dqstring = /"[^"]*"/ ;
function_def = symbol '(' parameters ')' ;
symbol = /([A-Za-z][A-Za-z0-9_]*)/ ;
parameters = [ expr ] { ',' expr } ;
node.jsのimportを参考にこんな感じになっている。
名前付き import: (*$ import { export1, export2 } from "module-name" $*)
デフォルトの import: (*$ import defaultExport from "module-name" $*)
こうすることで、
(*$ export {value_cell,time_stamp,date,number,boolean,sqstring as singleQuoteString,text} from "CELL_VALUE" $*)
値EBNF側で、{非}終端記号をindexedDBに書き込むtype名でexportする様にすれば、
(*$ import defaultExport from "CELL_VALUE" $*)
importする側はdefaultExportだけで済む。
テキストをガリガリしないでEBNFなimportParserとexportParserクラスを追加し、なんとか最低限の文法(上の2つダケ)を実装した。
(*$ SEQUENCE SEPARATOR IS NOT REQUIRED $*)
(* EXPORT *)
exportSyntax = "export" members "from" ebnf_export_name;
members = member | "{" member { "," member } "}" ;
member = symbol { "as" alias } ;
alias = symbol;
ebnf_export_name = dqstring;
dqstring = /"[^"]*"/ ;
symbol = /([A-Za-z][A-Za-z0-9_]*)/ ;
(*$ SEQUENCE SEPARATOR IS NOT REQUIRED $*)
(* IMPORT *)
importSyntax = "import" ( defaultExport | members ) "from" ebnf_export_name ;
defaultExport = "defaultExport" ;
members = member | "{" member { "," member } "}" ;
member = symbol { "as" alias } ;
alias = symbol;
ebnf_export_name = dqstring;
dqstring = /"[^"]*"/ ;
symbol = /([A-Za-z][A-Za-z0-9_]*)/ ;
これらのEBNFをパースする時点ではまだimportもexportも未登録状態なので、各自がexport処理し、ebnfParserのparseの中で直接importとexportをimportさせた。
当初はebnfParser.#parse内でEBNFテキスト中のexportを処理させたが、cell_valueやcell_exprのパーサのマップを反映する前にimportしてしまったので計算するデータが全て文字になっていたから、各クラスで#makeParser後にebnfParser.exportメソッドを呼びださせた。
後、できるだけ、return [……] や {……} を、return new xxxx(….) に書換え。
なんでもEBNF化すれば良い訳ではないけれど、これに限ってはトコトンEBNF化しないと・・・