From e025778538384bc6084af8cb1048762bddb15fc7 Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Mon, 1 Jun 2026 21:29:33 +0200 Subject: [PATCH] formatter script for local syntax files --- Makefile | 13 +- cmd/treerack/foo_test.treerack | 2 +- .../example}/acalc/acalc.treerack | 8 +- {docs/examples => doc/example}/acalc/go.mod | 0 {docs/examples => doc/example}/acalc/main.go | 0 .../examples => doc/example}/acalc/parser.go | 0 {docs/examples => doc/example}/json.treerack | 2 +- doc/example/keyval.treerack | 14 + doc/example/mml-exp.treerack | 504 ++++++++++++++++ doc/example/mml-exp2.treerack | 231 +++++++ doc/example/mml-exp3.treerack | 232 ++++++++ doc/example/mml.treerack | 225 +++++++ .../examples => doc/example}/scheme.treerack | 5 +- {docs/examples => doc/example}/sexpr.treerack | 2 +- {docs/examples => doc/example}/test.mml | 0 {docs => doc}/manual.md | 6 +- {docs => doc}/syntax.md | 4 +- docs/examples/keyval.treerack | 20 - docs/examples/mml-exp.treerack | 562 ------------------ docs/examples/mml-exp2.treerack | 271 --------- docs/examples/mml-exp3.treerack | 271 --------- docs/examples/mml.treerack | 266 --------- errors_test.go | 2 +- format.go | 5 + format_test.go | 3 +- head.go | 2 +- headexported.go | 2 +- json_test.go | 4 +- keyval_test.go | 2 +- mml_test.go | 2 +- mmlexp2_test.go | 2 +- mmlexp3_test.go | 2 +- mmlexp_test.go | 6 +- readme.md | 8 +- scheme_test.go | 2 +- {scripts => script}/cmdreadme.go | 0 {scripts => script}/createhead.go | 2 +- {scripts => script}/docreflect.go | 0 script/format.go | 46 ++ {scripts => script}/man.go | 0 sexpr_test.go | 2 +- syntax.go | 2 +- syntax.treerack | 122 ++-- 43 files changed, 1362 insertions(+), 1492 deletions(-) rename {docs/examples => doc/example}/acalc/acalc.treerack (91%) rename {docs/examples => doc/example}/acalc/go.mod (100%) rename {docs/examples => doc/example}/acalc/main.go (100%) rename {docs/examples => doc/example}/acalc/parser.go (100%) rename {docs/examples => doc/example}/json.treerack (85%) create mode 100644 doc/example/keyval.treerack create mode 100644 doc/example/mml-exp.treerack create mode 100644 doc/example/mml-exp2.treerack create mode 100644 doc/example/mml-exp3.treerack create mode 100644 doc/example/mml.treerack rename {docs/examples => doc/example}/scheme.treerack (66%) rename {docs/examples => doc/example}/sexpr.treerack (78%) rename {docs/examples => doc/example}/test.mml (100%) rename {docs => doc}/manual.md (98%) rename {docs => doc}/syntax.md (98%) delete mode 100644 docs/examples/keyval.treerack delete mode 100644 docs/examples/mml-exp.treerack delete mode 100644 docs/examples/mml-exp2.treerack delete mode 100644 docs/examples/mml-exp3.treerack delete mode 100644 docs/examples/mml.treerack rename {scripts => script}/cmdreadme.go (100%) rename {scripts => script}/createhead.go (97%) rename {scripts => script}/docreflect.go (100%) create mode 100644 script/format.go rename {scripts => script}/man.go (100%) diff --git a/Makefile b/Makefile index f8e2e90..bb73590 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ default: build .build/head.go: $(sources) .build go fmt ./... - go run scripts/createhead.go -- \ + go run script/createhead.go -- \ char.go \ sequence.go \ choice.go \ @@ -36,7 +36,7 @@ head.go: .build/head.go .build/headexported.go: $(sources) .build go fmt ./... - go run scripts/createhead.go --exported -- \ + go run script/createhead.go --exported -- \ char.go \ sequence.go \ choice.go \ @@ -75,12 +75,12 @@ lib: $(sources) head.go headexported.go internal/self/self.go go build ./internal/self cmd/treerack/docreflect.gen.go: $(sources) .build - go run scripts/docreflect.go > .build/docreflect.gen.go + go run script/docreflect.go > .build/docreflect.gen.go go fmt .build/docreflect.gen.go mv .build/docreflect.gen.go cmd/treerack cmd/treerack/readme.md: $(sources) cmd/treerack/docreflect.gen.go - go run scripts/cmdreadme.go ./cmd/treerack > cmd/treerack/readme.md || \ + go run script/cmdreadme.go ./cmd/treerack > cmd/treerack/readme.md || \ rm cmd/treerack/readme.md .build/treerack: $(sources) lib cmd/treerack/docreflect.gen.go .build @@ -88,7 +88,7 @@ cmd/treerack/readme.md: $(sources) cmd/treerack/docreflect.gen.go .build/treerack.1: $(sources) cmd/treerack/docreflect.gen.go .build - go run scripts/man.go $(version) $(release_date) > .build/treerack.1 || \ + go run script/man.go $(version) $(release_date) > .build/treerack.1 || \ rm .build/treerack.1 build: lib cmd/treerack/readme.md .build/treerack .build/treerack.1 @@ -135,8 +135,9 @@ cpu.out: $(sources) cpu: cpu.out go tool pprof -top cpu.out -fmt: $(sources) head.go headexported.go internal/self/self.go cmd/treerack/docreflect.gen.go +fmt: $(sources) $(parsers) head.go headexported.go internal/self/self.go cmd/treerack/docreflect.gen.go go fmt ./... + go run script/format.go $(parsers) $(prefix)/bin/treerack: .build/treerack mkdir -p $(prefix)/bin diff --git a/cmd/treerack/foo_test.treerack b/cmd/treerack/foo_test.treerack index 5abc475..2dbb5d1 100644 --- a/cmd/treerack/foo_test.treerack +++ b/cmd/treerack/foo_test.treerack @@ -1 +1 @@ -foo = "bar" +foo = "bar"; diff --git a/docs/examples/acalc/acalc.treerack b/doc/example/acalc/acalc.treerack similarity index 91% rename from docs/examples/acalc/acalc.treerack rename to doc/example/acalc/acalc.treerack index 71caf7c..7d3f06c 100644 --- a/docs/examples/acalc/acalc.treerack +++ b/doc/example/acalc/acalc.treerack @@ -5,7 +5,7 @@ ignore:ws = " " | [\t] | [\r] | [\n]; // between the digits and the delimiters. We support integers, floating point numbers, and floating point // numbers with their exponential notation. We don't support arbitrary leading zeros to avoid confusion with the // octal representation of numbers, which is not supported here. -num:nows = "-"? ("0" | [1-9][0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; +num:nows = "-"? ("0" | [1-9] [0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; // define the supported operators: add = "+"; @@ -30,8 +30,8 @@ operand1:alias = operand0 | binary0; // using the prioritized operators, we can define the prioritized binary expressions. We support a + b + c, and // not only a + b. -binary0 = operand0 (op0 operand0)+; -binary1 = operand1 (op1 operand1)+; +binary0 = operand0 (op0 operand0)+; +binary1 = operand1 (op1 operand1)+; binary:alias = binary0 | binary1; // let's define, what an expression can be. Notice the recursion along expression and group. @@ -44,4 +44,4 @@ expression:alias = num | group | binary; // // Note that we don't need to use the :root flag here, because it is our last definition, and this means that // the expression is the root parser of the syntax. -result = expression | "exit" +result = expression | "exit"; diff --git a/docs/examples/acalc/go.mod b/doc/example/acalc/go.mod similarity index 100% rename from docs/examples/acalc/go.mod rename to doc/example/acalc/go.mod diff --git a/docs/examples/acalc/main.go b/doc/example/acalc/main.go similarity index 100% rename from docs/examples/acalc/main.go rename to doc/example/acalc/main.go diff --git a/docs/examples/acalc/parser.go b/doc/example/acalc/parser.go similarity index 100% rename from docs/examples/acalc/parser.go rename to doc/example/acalc/parser.go diff --git a/docs/examples/json.treerack b/doc/example/json.treerack similarity index 85% rename from docs/examples/json.treerack rename to doc/example/json.treerack index 10f0dde..2670227 100644 --- a/docs/examples/json.treerack +++ b/doc/example/json.treerack @@ -4,7 +4,7 @@ true = "true"; false = "false"; null = "null"; string:nows = "\"" ([^\\"\b\f\n\r\t] | "\\" (["\\/bfnrt] | "u" [0-9a-f]{4}))* "\""; -number:nows = "-"? ("0" | [1-9][0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; +number:nows = "-"? ("0" | [1-9] [0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; entry = string ":" expression; object = "{" (entry ("," entry)*)? "}"; array = "[" (expression ("," expression)*)? "]"; diff --git a/doc/example/keyval.treerack b/doc/example/keyval.treerack new file mode 100644 index 0000000..3f720ee --- /dev/null +++ b/doc/example/keyval.treerack @@ -0,0 +1,14 @@ +whitespace:ws = [ \b\f\r\t\v]; +comment-line:alias = "#" [^\n]*; +comment = comment-line ("\n" comment-line)*; +quoted:alias:nows = "\"" ([^\\"] | "\\" .)* "\""; +word:alias:nows = ([^\\"\n=#.\[\] \b\f\r\t\v] | "\\" .)+; +symbol = word+ | quoted; +key-form:alias = symbol ("." symbol)*; +key = key-form; +group-key = (comment "\n")? "[" key-form "]"; +value-chars:alias:nows = ([^\\"\n=# \b\f\r\t\v] | "\\" .)+; +value = value-chars+ | quoted; +key-val = (comment "\n")? (key | key? "=" value?) comment-line?; +entry:alias = group-key | key-val; +doc:root = (entry | comment-line | "\n")*; diff --git a/doc/example/mml-exp.treerack b/doc/example/mml-exp.treerack new file mode 100644 index 0000000..fc53335 --- /dev/null +++ b/doc/example/mml-exp.treerack @@ -0,0 +1,504 @@ +// whitespace is ignored except for \n which is only ignored +// most of the time, but can serve as separator in: +// - list +// - struct +// - function args +// - statements +// - list, struct and function type constraints +// +// comments are not ignored because they are needed during formatting +ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; +wsc:ws = comment; +nl:alias = "\n"; + +// comments can be line or block comments +// indentation can hold meaning +line-comment-content:nows = [^\n]*; +line-comment:alias:nows = "//" line-comment-content; +block-comment-content:nows = ([^*] | "*" [^/])*; // TODO: why is the :nows required here if it is there for block-comment? +block-comment:alias:nows = "/*" block-comment-content "*/"; +comment-part:alias = line-comment | block-comment; +comment:alias = comment-part ("\n"? comment-part)*; +decimal-digit:alias = [0-9]; +octal-digit:alias = [0-7]; +hexa-digit:alias = [0-9a-fA-F]; + +// interger examples: 42, 0666, 0xfff +decimal:alias:nows = [1-9] decimal-digit*; +octal:alias:nows = "0" octal-digit*; +hexa:alias:nows = "0" [xX] hexa-digit+; +int = decimal | octal | hexa; + +// float examples: .0, 0., 3.14, 1E-12 +exponent:alias:nows = [eE] [+\-]? decimal-digit+; +float:nows = decimal-digit+ "." decimal-digit* exponent? + | "." decimal-digit+ exponent? + | decimal-digit+ exponent; + +// string example: "Hello, world!" +// only \ and " need to be escaped, e.g. allows new lines +// common escaped chars get unescaped, the rest gets unescaped to themselves +string:nows = "\"" ([^\\"] | "\\" .)* "\""; +true = "true"; +false = "false"; +bool:alias = true | false; + +// symbols normally can have only \w chars: fooBar_baz +// basic symbols cannot start with a digit +// some positions allow strings to be used as symbols, e.g: let "123" 123 +// when this is not possible, dynamic symbols need to be used, but they are +// not allowed in every case, e.g: {symbol(foo()): "bar"} +// TODO: needs decision log for dynamic symbol +// TODO: exclude keywords +// +// dynamic symbol decision log: +// - every value is equatable +// - structs can act as hashtables (optimization is transparent) +// - in structs, must differentiate between symbol and value of a symbol when used as a key +// - js style [a] would be enough for the structs +// - the variables in a scope are like fields in a struct +// - [a] would be ambigous with the list as an expression +// - a logical loophole is closed with symbol(a) +// - dynamic-symbols need to be handled differently in match expressions and type expressions +symbol:nows = [a-zA-Z_] [a-zA-Z_0-9]*; +static-symbol:alias = symbol | string; +dynamic-symbol = "symbol" "(" nl* expression nl* ")"; +symbol-expression:alias = static-symbol | dynamic-symbol; + +// TODO: what happens when a dynamic symbol gets exported? + +// list items are separated by comma or new line (or both) +/* + [] + [a, b, c] + [ + a + b + c + ] + [1, 2, a..., [b, c], [d, [e]]...] +*/ +spread-expression = primary-expression "..."; +list-sep:alias = ("," | "\n") (nl | ",")*; +list-item:alias = expression | spread-expression; +expression-list:alias = list-item (list-sep list-item)*; + +// list example: [1, 2, 3] +// lists can be constructed with other lists: [l1..., l2...] +list-fact:alias = "[" (nl | ",")* expression-list? (nl | ",")* "]"; +list = list-fact; +mutable-list = "~" nl* list-fact; +indexer-symbol = "[" nl* expression nl* "]"; +entry = (symbol-expression | indexer-symbol) nl* ":" nl* expression; +entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; +struct-fact:alias = "{" (nl | ",")* entry-list? (nl | ",")* "}"; +struct = struct-fact; +mutable-struct = "~" nl* struct-fact; +channel = "<>" | "<" nl* int nl* ">"; + +// and-expression:doc = "and" "(" (nl | ",")* expression-list? (nl | ",")* ")"; +// or-expression:doc = "or" "(" (nl | ",")* expression-list? (nl | ",")* ")"; + +argument-list:alias = static-symbol (list-sep static-symbol)*; +collect-symbol = "..." nl* static-symbol; +function-fact:alias = "(" (nl | ",")* argument-list? (nl | ",")* collect-symbol? (nl | ",")* ")" nl* expression; +function = "fn" nl* function-fact; // can it ever cause a conflict with call and grouping? +effect = "fn" nl* "~" nl* function-fact; + +/* +a[42] +a[3:9] +a[:9] +a[3:] +a[b][c][d] +a.foo +a."foo" +a.symbol(foo) +*/ +range-from = expression; +range-to = expression; +range-expression:alias = range-from? nl* ":" nl* range-to?; +indexer-expression:alias = expression | range-expression; +expression-indexer:alias = primary-expression "[" nl* indexer-expression nl* "]"; +symbol-indexer:alias = primary-expression nl* "." nl* symbol-expression; +indexer = expression-indexer | symbol-indexer; +function-application = primary-expression "(" (nl | ",")* expression-list? (nl | ",")* ")"; +if = "if" + nl* + expression + nl* + block + (nl* "else" nl* "if" nl* expression nl* block)* + (nl* "else" nl* block)?; +default = "default" nl* ":"; +default-line:alias = default (nl | ";")* statement?; +case = "case" nl* expression nl* ":"; +case-line:alias = case ";"* statement?; +switch = "switch" + nl* + expression? + nl* + "{" + (nl | ";")* + ((case-line | default-line) (sep (case-line | default-line | statement))*)? + (nl | ";")* + "}"; +int-type = "int"; +float-type = "float"; +string-type = "string"; +bool-type = "bool"; +error-type = "error"; + +/* +support: + + go { + foo() + bar() + } + + go { for { f() } } + go func() { for { f() } }() + fn f() { go f() }; go f() + +and not: + + go for {foo()} + +or: + + go for foo() + +because we don't know what the arguments are +*/ + +/* + + fn foo() { + bar() + baz() + } + let qux foo() + +equivalent to: + + let qux { + bar() + baz() + } +*/ + +primitive-type:alias = int-type | float-type | string-type | bool-type | error-type; +type-alias-name:alias = static-symbol; +static-range-from = int; +static-range-to = int; +static-range-expression:alias = static-range-from? nl* ":" nl* static-range-to?; +items-quantifier = int | static-range-expression; + +// TODO: maybe this can be confusing with matching constants. Shall we support matching constants, values? + +items-type = items-quantifier + | type-set (nl* ":" nl* items-quantifier)? + | static-symbol nl* type-set (nl* ":" nl* items-quantifier)?; +destructure-item = type-set | static-symbol nl* type-set; +collect-destructure-item = "..." nl* destructure-item? (nl* ":" items-quantifier)?; +list-destructure-type = destructure-item + (list-sep destructure-item)* + (list-sep collect-destructure-item)? + | collect-destructure-item; +list-type-fact:alias = "[" (nl | ",")* (items-type | list-destructure-type)? (nl | ",")* "]"; +list-type = list-type-fact; +mutable-list-type = "~" nl* list-type-fact; +destructure-match-item = match-set + | static-symbol nl* match-set + | static-symbol nl* static-symbol nl* match-set; +collect-destructure-match-item = "..." nl* destructure-match-item? (nl* ":" items-quantifier)?; +list-destructure-match = destructure-match-item + (list-sep destructure-match-item)* + (list-sep collect-destructure-match-item)? + | collect-destructure-match-item; +list-match-fact:alias = "[" (nl | ",")* (list-destructure-match | items-type)? (nl | ",")* "]"; +list-match = list-match-fact; +mutable-list-match = "~" nl* list-match; +entry-type = static-symbol (nl* ":" nl* destructure-item)?; +entry-types:alias = entry-type (list-sep entry-type)*; +struct-type-fact:alias = "{" (nl | ",")* entry-types? (nl | ",")* "}"; +struct-type = struct-type-fact; +mutable-struct-type = "~" nl* struct-type-fact; +entry-match = static-symbol (nl* ":" nl* destructure-match-item)?; +entry-matches:alias = entry-match (list-sep entry-match)*; +struct-match-fact:alias = "{" (nl | ",")* entry-matches? (nl | ",")* "}"; +struct-match = struct-match-fact; +mutable-struct-match = "~" nl* struct-match-fact; +arg-type = type-set | static-symbol nl* type-set; +args-type:alias = arg-type (list-sep arg-type)*; +function-type-fact:alias = "(" nl* args-type? nl* ")" (type-set | static-symbol type-set)?; +function-type = "fn" nl* function-type-fact; +effect-type = "fn" nl* "~" nl* function-type-fact; + +// TODO: heavy naming crime + +receive-direction = "receive"; +send-direction = "send"; +channel-type = "<" nl* (receive-direction | send-direction)? nl* destructure-item? nl* ">"; +type-fact-group:alias = "(" nl* type-fact nl* ")"; +type-fact:alias = primitive-type + | type-alias-name + | list-type + | mutable-list-type + | struct-type + | mutable-struct-type + | function-type + | effect-type + | channel-type + | type-fact-group; +type-set:alias = type-fact (nl* "|" nl* type-fact)*; +type-expression:alias = type-set | static-symbol type-set; +match-fact:alias = list-match | mutable-list-match | struct-match | mutable-struct-match; +match-set:alias = type-set | match-fact; +match-expression:alias = match-set | static-symbol match-set; +match-case = "case" nl* match-expression nl* ":"; +match-case-line:alias = match-case ";"* statement?; +match = "match" + nl* + expression + nl* + "{" + (nl | ";")* + ( (match-case-line | default-line) + (sep (match-case-line | default-line | statement))* + )? + (nl | ";")* + "}"; +conditional:alias = if | switch | match; +receive-call = "receive" "(" (nl | ",")* expression (nl | ",")* ")"; +receive-op = "<-" primary-expression; +receive-expression-group:alias = "(" nl* receive-expression nl* ")"; +receive-expression:alias = receive-call | receive-op | receive-expression-group; +receive-assign-capture:alias = assignable nl* ("=" nl*)? receive-expression; +receive-assignment = "set" nl* receive-assign-capture; +receive-assignment-equal = assignable nl* "=" nl* receive-expression; +receive-capture:alias = symbol-expression nl* ("=" nl*)? receive-expression; +receive-definition = "let" nl* receive-capture; +receive-mutable-definition = "let" nl* "~" nl* receive-capture; +receive-statement:alias = receive-assignment | receive-definition; +send-call:alias = "send" "(" (nl | ",")* expression list-sep expression (nl | ",")* ")"; +send-op:alias = primary-expression "<-" expression; +send-call-group:alias = "(" nl* send nl* ")"; +send = send-call | send-op | send-call-group; +close = "close" "(" (nl | ",")* expression (nl | ",")* ")"; +communication-group:alias = "(" nl* communication nl* ")"; +communication:alias = receive-expression | receive-statement | send | communication-group; +select-case = "case" nl* communication nl* ":"; +select-case-line:alias = select-case ";"* statement?; +select = "select" + nl* + "{" + (nl | ";")* + ( (select-case-line | default-line) + (sep (select-case-line | default-line | statement))* + )? + (nl | ";")* + "}"; +go = "go" nl* (function-application | block); + +/* +require . = "mml/foo" +require bar = "mml/foo" +require . "mml/foo" +require bar "mml/foo" +require "mml/foo" +require ( + . = "mml/foo" + bar = "mml/foo" + . "mml/foo" + bar "mml/foo" + "mml/foo" +) +require () +*/ +require-inline = "."; +require-fact = string | (static-symbol | require-inline) (nl* "=")? nl* string; +require-facts:alias = require-fact (list-sep require-fact)*; +require-statement:alias = "require" nl* require-fact; +require-statement-group:alias = "require" "(" (nl | ",")* require-facts? (nl | ",")* ")"; +require = require-statement | require-statement-group; +panic = "panic" "(" (nl | ",")* expression (nl | ",")* ")"; +recover = "recover" "(" (nl | ",")* ")"; +block = "{" (nl | ";")* statements? (nl | ";")* "}"; +expression-group:alias = "(" nl* expression nl* ")"; +primary-expression:alias = int + | float + | string + | bool + | symbol + | dynamic-symbol + | list + | mutable-list + | struct + | mutable-struct + | channel + // | and-expression // only documentation + // | or-expression // only documentation + | function + | effect + | indexer + | function-application + // pseudo-expression + | conditional + // pseudo-expression + | receive-call + | select + // pseudo-expression + | recover + | block + // pseudo-expression + | expression-group; +plus = "+"; +minus = "-"; +logical-not = "!"; +binary-not = "^"; +unary-operator:alias = plus | minus | logical-not | binary-not; +unary-expression = unary-operator primary-expression | receive-op; +mul = "*"; +div = "/"; +mod = "%"; +lshift = "<<"; +rshift = ">>"; +binary-and = "&"; +and-not = "&^"; +add = "+"; +sub = "-"; +binary-or = "|"; +xor = "^"; +eq = "=="; +not-eq = "!="; +less = "<"; +less-or-eq = "<="; +greater = ">"; +greater-or-eq = ">="; +logical-and = "&&"; +logical-or = "||"; +chain = "->"; +binary-op0:alias = mul | div | mod | lshift | rshift | binary-and | and-not; +binary-op1:alias = add | sub | binary-or | xor; +binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; +binary-op3:alias = logical-and; +binary-op4:alias = logical-or; +binary-op5:alias = chain; +operand0:alias = primary-expression | unary-expression; +operand1:alias = operand0 | binary0; +operand2:alias = operand1 | binary1; +operand3:alias = operand2 | binary2; +operand4:alias = operand3 | binary3; +operand5:alias = operand4 | binary4; +binary0 = operand0 (binary-op0 operand0)+; +binary1 = operand1 (binary-op1 operand1)+; +binary2 = operand2 (binary-op2 operand2)+; +binary3 = operand3 (binary-op3 operand3)+; +binary4 = operand4 (binary-op4 operand4)+; +binary5 = operand5 (binary-op5 operand5)+; +binary-expression:alias = binary0 | binary1 | binary2 | binary3 | binary4 | binary5; +ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; +expression:alias = primary-expression | unary-expression | binary-expression | ternary-expression; + +// TODO: code() +// TODO: observability + +break = "break"; +continue = "continue"; +loop-control:alias = break | continue; +in-expression = static-symbol nl* "in" nl* (expression | range-expression); +loop-expression = expression | in-expression; +loop = "for" nl* (block | loop-expression nl* block); + +/* +a = b +set c = d +set e f +set ( + g = h + i j +) +*/ +assignable:alias = symbol-expression | indexer; +assign-capture = assignable nl* ("=" nl*)? expression; +assign-set:alias = "set" nl* assign-capture; +assign-equal = assignable nl* "=" nl* expression; +assign-captures:alias = assign-capture (list-sep assign-capture)*; +assign-group:alias = "set" nl* "(" (nl | ",")* assign-captures? (nl | ",")* ")"; +assignment = assign-set | assign-equal | assign-group; + +/* +let a = b +let c d +let ~ e = f +let ~ g h +let ( + i = j + k l + ~ m = n + ~ o p +) +let ~ ( + q = r + s t +) +*/ +value-capture-fact:alias = symbol-expression nl* ("=" nl*)? expression; +value-capture = value-capture-fact; +mutable-capture = "~" nl* value-capture-fact; +value-definition = "let" nl* (value-capture | mutable-capture); +value-captures:alias = value-capture (list-sep value-capture)*; +mixed-captures:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*; +value-definition-group = "let" nl* "(" (nl | ",")* mixed-captures? (nl | ",")* ")"; +mutable-definition-group = "let" nl* "~" nl* "(" (nl | ",")* value-captures? (nl | ",")* ")"; + +/* +fn a() b +fn ~ c() d +fn ( + e() f + ~ g() h +) +fn ~ ( + i() + j() +) +*/ +function-definition-fact:alias = static-symbol nl* function-fact; +function-capture = function-definition-fact; +effect-capture = "~" nl* function-definition-fact; +function-definition = "fn" nl* (function-capture | effect-capture); +function-captures:alias = function-capture (list-sep function-capture)*; +mixed-function-captures:alias = (function-capture | effect-capture) + (list-sep (function-capture | effect-capture))*; +function-definition-group = "fn" nl* "(" (nl | ",")* mixed-function-captures? (nl | ",")* ")"; +effect-definition-group = "fn" nl* "~" nl* "(" (nl | ",")* function-captures? (nl | ",")* ")"; +definition:alias = value-definition + | value-definition-group + | mutable-definition-group + | function-definition + | function-definition-group + | effect-definition-group; +type-alias = "type" nl* "alias" nl* static-symbol nl* type-set; +type-constraint = "type" nl* static-symbol nl* type-set; +statement-group:alias = "(" nl* statement nl* ")"; +statement:alias = send + | close + | panic + | require + | loop-control + | go + | loop + | assignment + | definition + | expression + | type-alias + | type-constraint + | statement-group; +shebang-command = [^\n]*; +shebang = "#!" shebang-command "\n"; +sep:alias = (";" | "\n") (nl | ";")*; +statements:alias = statement (sep statement)*; +mml:root = shebang? (nl | ";")* statements? (nl | ";")*; diff --git a/doc/example/mml-exp2.treerack b/doc/example/mml-exp2.treerack new file mode 100644 index 0000000..1aa8596 --- /dev/null +++ b/doc/example/mml-exp2.treerack @@ -0,0 +1,231 @@ +ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; +wsc:ws = comment; +nl:alias = "\n"; +line-comment-content:nows = [^\n]*; +line-comment:alias:nows = "//" line-comment-content; +block-comment-content:nows = ([^*] | "*" [^/])*; +block-comment:alias:nows = "/*" block-comment-content "*/"; +comment-part:alias = line-comment | block-comment; +comment:alias = comment-part (nl? comment-part)*; +decimal-digit:alias = [0-9]; +octal-digit:alias = [0-7]; +hexa-digit:alias = [0-9a-fA-F]; +decimal:alias:nows = [1-9] decimal-digit*; +octal:alias:nows = "0" octal-digit*; +hexa:alias:nows = "0" [xX] hexa-digit+; +int = decimal | octal | hexa; +exponent:alias:nows = [eE] [+\-]? decimal-digit+; +float:nows = decimal-digit+ "." decimal-digit* exponent? + | "." decimal-digit+ exponent? + | decimal-digit+ exponent; +string:nows = "\"" ([^\\"] | "\\" .)* "\""; +true = "true"; +false = "false"; +bool:alias = true | false; +symbol:nows = [a-zA-Z_] [a-zA-Z_0-9]*; +spread-expression = primary-expression "..."; +list-sep:alias = (nl | ",")+; +list-item:alias = expression | spread-expression; +expression-list:alias = list-item (list-sep list-item)*; +list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; +list = list-fact; +mutable-list = "~" nl* list-fact; +expression-key = "[" nl* expression nl* "]"; +entry = (symbol | string | expression-key) nl* ":" nl* expression; +entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; +struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; +struct = struct-fact; +mutable-struct = "~" nl* struct-fact; +parameter-list:alias = symbol (list-sep symbol)*; +collect-parameter = "..." nl* symbol; +return = "return" (nl* expression)?; +block = "{" sep? statement-list? sep? "}"; +function-fact:alias = "(" + list-sep? + (parameter-list | parameter-list list-sep collect-parameter | collect-parameter)? + list-sep? + ")" + nl* + (simple-statement | block); +function = "fn" nl* function-fact; +effect = "fn" nl* "~" nl* function-fact; +range-from = expression; +range-to = expression; +range:alias = range-from? nl* ":" nl* range-to?; +symbol-index = "." nl* symbol; +expression-index = "[" nl* expression nl* "]"; +index:alias = symbol-index | expression-index; +index-list:alias = index (nl* index)?; +indexer = primary-expression nl* index-list; +function-application = primary-expression "(" list-sep? expression-list? list-sep? ")"; +expression-group:alias = "(" nl* expression nl* ")"; +primary-expression:alias = int + | float + | string + | bool + | receive + | symbol + | list + | mutable-list + | struct + | mutable-struct + | function + | effect + | indexer + | function-application + | expression-group; +binary-not = "^"; +binary-and = "&"; +binary-or = "|"; +xor = "^"; +and-not = "&^"; +lshift = "<<"; +rshift = ">>"; +plus = "+"; +minus = "-"; +mul = "*"; +div = "/"; +mod = "%"; +add = "+"; +sub = "-"; +logical-not = "!"; +eq = "=="; +not-eq = "!="; +less = "<"; +less-or-eq = "<="; +greater = ">"; +greater-or-eq = ">="; +logical-and = "&&"; +logical-or = "||"; +chain:alias = "->"; +unary-operator:alias = plus | minus | binary-not | logical-not; +unary-expression = unary-operator primary-expression; +binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; +binary-op1:alias = binary-or | xor | add | sub; +binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; +binary-op3:alias = logical-and; +binary-op4:alias = logical-or; +operand0:alias = primary-expression | unary-expression; +operand1:alias = operand0 | binary0; +operand2:alias = operand1 | binary1; +operand3:alias = operand2 | binary2; +operand4:alias = operand3 | binary3; +operand5:alias = operand4 | binary4; +binary0 = operand0 (nl* binary-op0 nl* operand0)+; +binary1 = operand1 (nl* binary-op1 nl* operand1)+; +binary2 = operand2 (nl* binary-op2 nl* operand2)+; +binary3 = operand3 (nl* binary-op3 nl* operand3)+; +binary4 = operand4 (nl* binary-op4 nl* operand4)+; +binary-expression:alias = binary0 | binary1 | binary2 | binary3 | binary4; +ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; +chainingOperand:alias = primary-expression | unary-expression | binary-expression | ternary-expression; +chaining = chainingOperand (nl* chain nl* chainingOperand)+; +expression:alias = primary-expression + | unary-expression + | binary-expression + | ternary-expression + | chaining; +if = "if" + nl* + expression + nl* + block + (nl* "else" nl* "if" nl* expression nl* block)* + (nl* "else" nl* block)?; + +// TODO: empty switch not parsed +default = "default" nl* ":"; +default-line:alias = default ";"* statement?; +case = "case" nl* expression nl* ":"; +case-line:alias = case ";"* statement?; +switch = "switch" + nl* + expression? + nl* + "{" + sep? + ((case-line | default-line) (sep (case-line | default-line | statement))*)? + sep? + "}"; +send = "send" nl* primary-expression nl* primary-expression; +receive = "receive" nl* primary-expression; +receive-definition = symbol nl* receive; +communication:alias = send | receive | receive-definition; +select-case = "case" nl* communication nl* ":"; +select-case-line:alias = select-case ";"* statement?; +select = "select" + nl* + "{" + sep? + ( (select-case-line | default-line) + (sep (select-case-line | default-line | statement))* + )? + sep? + "}"; +go = "go" nl* function-application; +defer = "defer" nl* function-application; +range-over-expression = symbol nl* "in" nl* (expression | range) | range; +loop-expression:alias = expression | range-over-expression; +loop = "for" ((nl* loop-expression)? nl* block | nl* block); + +// TODO: set(a b) +assign-capture:alias = primary-expression (nl* "=")? nl* expression; +assign-capture-list:alias = assign-capture (list-sep assign-capture)*; +assign-set:alias = "set" nl* assign-capture; +assign-eq:alias = primary-expression nl* "=" nl* expression; +assign-group:alias = "set" nl* "(" (list-sep assign-capture-list)? list-sep? ")"; +assignment = assign-set | assign-eq | assign-group; +value-capture-fact:alias = symbol (nl* "=")? nl* expression; +value-capture = value-capture-fact; +mutable-capture = "~" nl* value-capture-fact; +value-definition = "let" nl* (value-capture | mutable-capture); +mixed-capture-list:alias = (value-capture | mutable-capture) + (list-sep (value-capture | mutable-capture))*; +value-capture-list:alias = value-capture (list-sep value-capture)*; +value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; +mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; +function-definition-fact:alias = symbol nl* function-fact; +function-capture = function-definition-fact; +effect-capture = "~" nl* function-definition-fact; +function-definition = "fn" nl* (function-capture | effect-capture); +function-capture-list:alias = function-capture (list-sep function-capture)*; +mixed-function-capture-list:alias = (function-capture | effect-capture) + (list-sep (function-capture | effect-capture))*; +function-definition-group = "fn" nl* "(" list-sep? mixed-function-capture-list? list-sep? ")"; +effect-definition-group = "fn" nl* "~" nl* "(" list-sep? function-capture-list? list-sep? ")"; +definition:alias = value-definition + | value-definition-group + | mutable-definition-group + | function-definition + | function-definition-group + | effect-definition-group; + +// TODO: +// - use effect +// - rename to 'use' + +use-inline = "."; +use-fact = string | (symbol | use-inline) (nl* "=")? nl* string; +use-fact-list:alias = use-fact (list-sep use-fact)*; +use-statement:alias = "use" nl* use-fact; +use-statement-group:alias = "use" nl* "(" list-sep? use-fact-list? list-sep? ")"; +use = use-statement | use-statement-group; +export = "export" nl* definition; +simple-statement:alias = send | go | defer | assignment | simple-statement-group | expression; +simple-statement-group:alias = "(" nl* simple-statement nl* ")"; +statement:alias = return + | if + | switch + | select + | loop + | definition + | use + | export + | statement-group + | simple-statement; +statement-group:alias = "(" nl* statement nl* ")"; +sep:alias = (";" | nl)+; +statement-list:alias = statement (sep statement)*; +shebang-command = [^\n]*; +shebang = "#!" shebang-command "\n"; +mml:root = shebang? sep? statement-list? sep?; diff --git a/doc/example/mml-exp3.treerack b/doc/example/mml-exp3.treerack new file mode 100644 index 0000000..513e65d --- /dev/null +++ b/doc/example/mml-exp3.treerack @@ -0,0 +1,232 @@ +ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; +wsc:ws = comment; +nl:alias = "\n"; +wsep:alias = ws | nl; +line-comment-content:nows = [^\n]*; +line-comment:alias:nows = "//" line-comment-content; +block-comment-content:nows = ([^*] | "*" [^/])*; +block-comment:alias:nows = "/*" block-comment-content "*/"; +comment-part:alias = line-comment | block-comment; +comment:alias = comment-part (nl? comment-part)*; +decimal-digit:alias = [0-9]; +octal-digit:alias = [0-7]; +hexa-digit:alias = [0-9a-fA-F]; +decimal:alias:nows = [1-9] decimal-digit*; +octal:alias:nows = "0" octal-digit*; +hexa:alias:nows = "0" [xX] hexa-digit+; +int = decimal | octal | hexa; +exponent:alias:nows = [eE] [+\-]? decimal-digit+; +float:nows = decimal-digit+ "." decimal-digit* exponent? + | "." decimal-digit+ exponent? + | decimal-digit+ exponent; +string:nows = "\"" ([^\\"] | "\\" .)* "\""; +true = "true"; +false = "false"; +bool:alias = true | false; +symbol:nows = [a-zA-Z_] [a-zA-Z_0-9]*; +spread-expression = primary-expression "..."; +list-sep:alias = (nl | ",")+; +list-item:alias = expression | spread-expression; +expression-list:alias = list-item (list-sep list-item)*; +list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; +list = list-fact; +mutable-list = "~" nl* list-fact; +expression-key = "[" nl* expression nl* "]"; +entry = (symbol | string | expression-key) nl* ":" nl* expression; +entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; +struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; +struct = struct-fact; +mutable-struct = "~" nl* struct-fact; +parameter-list:alias = symbol (list-sep symbol)*; +collect-parameter = "..." nl* symbol; +return = "return" | "return" nl* expression; +block = "{" sep? statement-list? sep? "}"; +function-fact:alias = "(" + list-sep? + (parameter-list | parameter-list list-sep collect-parameter | collect-parameter)? + list-sep? + ")" + nl* + (expression | simple-statement | block); +function = "fn" nl* function-fact; +effect = "fn" nl* "~" nl* function-fact; +range-from = expression; +range-to = expression; +range:alias = range-from? nl* ":" nl* range-to?; +symbol-index = "." nl* symbol; +expression-index = "[" nl* expression nl* "]"; +range-index = "[" nl* range nl* "]"; +index:alias = symbol-index | expression-index | range-index; +index-list:alias = index (nl* index)?; +indexer = primary-expression nl* index-list; +application = primary-expression "(" list-sep? expression-list? list-sep? ")"; +expression-group:alias = "(" nl* expression nl* ")"; +primary-expression:alias = int + | float + | string + | bool + | symbol + | list + | mutable-list + | struct + | mutable-struct + | function + | effect + | indexer + | application + | receive + | expression-group; +binary-not = "^"; +binary-and = "&"; +binary-or = "|"; +xor = "^"; +and-not = "&^"; +lshift = "<<"; +rshift = ">>"; +plus = "+"; +minus = "-"; +mul = "*"; +div = "/"; +mod = "%"; +add = "+"; +sub = "-"; +logical-not = "!"; +eq = "=="; +not-eq = "!="; +less = "<"; +less-or-eq = "<="; +greater = ">"; +greater-or-eq = ">="; +logical-and = "&&"; +logical-or = "||"; +chain:alias = "->"; +unary-operator:alias = plus | minus | binary-not | logical-not; +unary-expression = unary-operator primary-expression; +binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; +binary-op1:alias = binary-or | xor | add | sub; +binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; +binary-op3:alias = logical-and; +binary-op4:alias = logical-or; +operand0:alias = primary-expression | unary-expression; +operand1:alias = operand0 | binary0; +operand2:alias = operand1 | binary1; +operand3:alias = operand2 | binary2; +operand4:alias = operand3 | binary3; +operand5:alias = operand4 | binary4; +binary0 = operand0 (nl* binary-op0 nl* operand0)+; +binary1 = operand1 (nl* binary-op1 nl* operand1)+; +binary2 = operand2 (nl* binary-op2 nl* operand2)+; +binary3 = operand3 (nl* binary-op3 nl* operand3)+; +binary4 = operand4 (nl* binary-op4 nl* operand4)+; +binary-expression:alias = binary0 | binary1 | binary2 | binary3 | binary4; +ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; +chainingOperand:alias = primary-expression | unary-expression | binary-expression | ternary-expression; +chaining = chainingOperand (nl* chain nl* chainingOperand)+; +expression:alias = primary-expression + | unary-expression + | binary-expression + | ternary-expression + | chaining; +if = "if" + nl* + expression + nl* + block + (nl* "else" nl* "if" nl* expression nl* block)* + (nl* "else" nl* block)?; + +// TODO: empty switch not parsed +default = "default" nl* ":"; +default-line:alias = default ";"* statement?; +case = "case" nl* expression nl* ":"; +case-line:alias = case ";"* statement?; +switch = "switch" + nl* + expression? + nl* + "{" + sep? + ((case-line | default-line) (sep (case-line | default-line | statement))*)? + sep? + "}"; +send = "send" nl* primary-expression nl* primary-expression; +receive = "receive" nl* primary-expression; +receive-definition = symbol nl* receive; +communication:alias = send | receive | receive-definition; +select-case = "case" nl* communication nl* ":"; +select-case-line:alias = select-case ";"* statement?; +select = "select" + nl* + "{" + sep? + ( (select-case-line | default-line) + (sep (select-case-line | default-line | statement))* + )? + sep? + "}"; +go = "go" nl* application; +defer = "defer" nl* application; +break = "break"; +continue = "continue"; +loop-control:alias = break | continue; +range-over = symbol nl* "in" nl* (expression | range) | range; +loop-expression:alias = expression | range-over; +loop = "for" ((nl* loop-expression)? nl* block | nl* block); +assignment = (symbol | indexer) nl* "=" nl* expression; +value-capture-fact:alias = symbol (nl* "=")? nl* expression; +value-capture = value-capture-fact; +mutable-capture = "~" nl* value-capture-fact; +value-definition = "let" nl* (value-capture | mutable-capture); +mixed-capture-list:alias = (value-capture | mutable-capture) + (list-sep (value-capture | mutable-capture))*; +value-capture-list:alias = value-capture (list-sep value-capture)*; +value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; +mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; +function-definition-fact:alias = symbol nl* function-fact; +function-capture = function-definition-fact; +effect-capture = "~" nl* function-definition-fact; +function-definition = "fn" nl* (function-capture | effect-capture); +function-capture-list:alias = function-capture (list-sep function-capture)*; +mixed-function-capture-list:alias = (function-capture | effect-capture) + (list-sep (function-capture | effect-capture))*; +function-definition-group = "fn" nl* "(" list-sep? mixed-function-capture-list? list-sep? ")"; +effect-definition-group = "fn" nl* "~" nl* "(" list-sep? function-capture-list? list-sep? ")"; +definition:alias = value-definition + | value-definition-group + | mutable-definition-group + | function-definition + | function-definition-group + | effect-definition-group; + +// TODO: +// - use effect + +use-inline = "."; +use-fact = string | (symbol | use-inline) (nl* "=")? nl* string; +use-fact-list:alias = use-fact (list-sep use-fact)*; +use-statement:alias = "use" nl* use-fact; +use-statement-group:alias = "use" nl* "(" list-sep? use-fact-list? list-sep? ")"; +use = use-statement | use-statement-group; +export = "export" nl* definition; +simple-statement:alias = send | go | defer | assignment | simple-statement-group; +simple-statement-group:alias = "(" nl* simple-statement nl* ")"; +statement:alias = simple-statement + | return + | if + | switch + | select + | loop + | loop-control + | definition + | use + | export + | application + // TODO: sendSomething() and sendSomething + | chaining + | statement-group; +statement-group:alias = "(" nl* statement nl* ")"; +sep:alias = (";" | nl)+; +statement-list:alias = statement (sep statement)*; +shebang-command = [^\n]*; +shebang = "#!" shebang-command "\n"; +mml:root = shebang? sep? statement-list? sep?; diff --git a/doc/example/mml.treerack b/doc/example/mml.treerack new file mode 100644 index 0000000..c87dd87 --- /dev/null +++ b/doc/example/mml.treerack @@ -0,0 +1,225 @@ +ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; +wsc:ws = comment; +nl:alias = "\n"; +line-comment-content:nows = [^\n]*; +line-comment:alias:nows = "//" line-comment-content; +block-comment-content:nows = ([^*] | "*" [^/])*; +block-comment:alias:nows = "/*" block-comment-content "*/"; +comment-part:alias = line-comment | block-comment; +comment:alias = comment-part ("\n"? comment-part)*; +decimal-digit:alias = [0-9]; +octal-digit:alias = [0-7]; +hexa-digit:alias = [0-9a-fA-F]; +decimal:alias:nows = [1-9] decimal-digit*; +octal:alias:nows = "0" octal-digit*; +hexa:alias:nows = "0" [xX] hexa-digit+; +int = decimal | octal | hexa; +exponent:alias:nows = [eE] [+\-]? decimal-digit+; +float:nows = decimal-digit+ "." decimal-digit* exponent? + | "." decimal-digit+ exponent? + | decimal-digit+ exponent; +string:nows = "\"" ([^\\"] | "\\" .)* "\""; +true = "true"; +false = "false"; +bool:alias = true | false; +symbol:nows = [a-zA-Z_] [a-zA-Z_0-9]*; +spread-expression = primary-expression "..."; +list-sep:alias = (nl | ",")+; +list-item:alias = expression | spread-expression; +expression-list:alias = list-item (list-sep list-item)*; +list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; +list = list-fact; +mutable-list = "~" nl* list-fact; +expression-key = "[" nl* expression nl* "]"; +entry = (symbol | string | expression-key) nl* ":" nl* expression; +entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; +struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; +struct = struct-fact; +mutable-struct = "~" nl* struct-fact; +channel = "<>" | "<" nl* expression nl* ">"; +parameter-list:alias = symbol (list-sep symbol)*; +collect-parameter = "..." nl* symbol; +return = "return" (nl* expression)?; +block = "{" sep? statement-list? sep? "}"; +function-fact:alias = "(" + list-sep? + parameter-list? + (list-sep collect-parameter)? + list-sep? + ")" + nl* + (simple-statement | block); +function = "fn" nl* function-fact; +effect = "fn" nl* "~" nl* function-fact; +range-from = expression; +range-to = expression; +range:alias = range-from? nl* ":" nl* range-to?; +simple-indexer:alias = primary-expression "[" nl* expression nl* "]"; +range-indexer:alias = primary-expression "[" nl* range nl* "]"; +expression-indexer = simple-indexer | range-indexer; +symbol-indexer = primary-expression nl* "." nl* symbol; +function-application = primary-expression "(" list-sep? expression-list? list-sep? ")"; +expression-group:alias = "(" nl* expression nl* ")"; +primary-expression:alias = int + | float + | string + | bool + | symbol + | list + | mutable-list + | struct + | mutable-struct + | channel + | function + | effect + | expression-indexer + | symbol-indexer + | function-application + | receive + | expression-group; +binary-not = "^"; +binary-and = "&"; +binary-or = "|"; +xor = "^"; +and-not = "&^"; +lshift = "<<"; +rshift = ">>"; +plus = "+"; +minus = "-"; +mul = "*"; +div = "/"; +mod = "%"; +add = "+"; +sub = "-"; +logical-not = "!"; +eq = "=="; +not-eq = "!="; +less = "<"; +less-or-eq = "<="; +greater = ">"; +greater-or-eq = ">="; +logical-and = "&&"; +logical-or = "||"; +chain:alias = "->"; +unary-operator:alias = plus | minus | binary-not | logical-not; +unary-expression = unary-operator primary-expression; +binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; +binary-op1:alias = binary-or | xor | add | sub; +binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; +binary-op3:alias = logical-and; +binary-op4:alias = logical-or; +operand0:alias = primary-expression | unary-expression; +operand1:alias = operand0 | binary0; +operand2:alias = operand1 | binary1; +operand3:alias = operand2 | binary2; +operand4:alias = operand3 | binary3; +operand5:alias = operand4 | binary4; +binary0 = operand0 (binary-op0 operand0)+; +binary1 = operand1 (binary-op1 operand1)+; +binary2 = operand2 (binary-op2 operand2)+; +binary3 = operand3 (binary-op3 operand3)+; +binary4 = operand4 (binary-op4 operand4)+; +chaining = operand5 (nl* chain nl* operand5)+; +binary-expression:alias = binary0 | binary1 | binary2 | binary3 | binary4 | chaining; +ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; +expression:alias = primary-expression + | unary-expression + | binary-expression + | ternary-expression; +if = "if" + nl* + expression + nl* + block + (nl* "else" nl* "if" nl* expression nl* block)* + (nl* "else" nl* block)?; +default = "default" nl* ":"; +default-line:alias = default ";"* statement?; +case = "case" nl* expression nl* ":"; +case-line:alias = case ";"* statement?; +switch = "switch" + nl* + expression? + nl* + "{" + sep? + ((case-line | default-line) (sep (case-line | default-line | statement))*)? + sep? + "}"; +receive = "<<>" primary-expression; +receive-definition = "let" nl* symbol nl* receive; +receive-assignment = "set" nl* symbol nl* receive; +receive-statement:alias = receive-definition | receive-assignment; +send = primary-expression "<<>" primary-expression; +communication:alias = receive | receive-statement | send; +select-case = "case" nl* communication nl* ":"; +select-case-line:alias = select-case ";"* statement?; +select = "select" + nl* + "{" + sep? + ( (select-case-line | default-line) + (sep (select-case-line | default-line | statement))* + )? + sep? + "}"; +go = "go" nl* function-application; +defer = "defer" nl* function-application; +range-over-expression = symbol nl* "in" nl* (expression | range) | range; +loop-expression:alias = expression | range-over-expression; +loop = "for" ((nl* loop-expression)? nl* block | nl* block); +assign-capture:alias = primary-expression (nl* "=")? nl* expression; +assign-capture-list:alias = assign-capture (list-sep assign-capture)*; +assign-set:alias = "set" nl* assign-capture; +assign-eq:alias = primary-expression nl* "=" nl* expression; +assign-group:alias = "set" nl* "(" (list-sep assign-capture-list)? list-sep? ")"; +assignment = assign-set | assign-eq | assign-group; +value-capture-fact:alias = symbol (nl* "=")? nl* expression; +value-capture = value-capture-fact; +mutable-capture = "~" nl* value-capture-fact; +value-definition = "let" nl* (value-capture | mutable-capture); +mixed-capture-list:alias = (value-capture | mutable-capture) + (list-sep (value-capture | mutable-capture))*; +value-capture-list:alias = value-capture (list-sep value-capture)*; +value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; +mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; +function-definition-fact:alias = symbol nl* function-fact; +function-capture = function-definition-fact; +effect-capture = "~" nl* function-definition-fact; +function-definition = "fn" nl* (function-capture | effect-capture); +function-capture-list:alias = function-capture (list-sep function-capture)*; +mixed-function-capture-list:alias = (function-capture | effect-capture) + (list-sep (function-capture | effect-capture))*; +function-definition-group = "fn" nl* "(" list-sep? mixed-function-capture-list? list-sep? ")"; +effect-definition-group = "fn" nl* "~" nl* "(" list-sep? function-capture-list? list-sep? ")"; +definition:alias = value-definition + | value-definition-group + | mutable-definition-group + | function-definition + | function-definition-group + | effect-definition-group; +require-inline = "."; +require-fact = string | (symbol | require-inline) (nl* "=")? nl* string; +require-fact-list:alias = require-fact (list-sep require-fact)*; +require-statement:alias = "require" nl* require-fact; +require-statement-group:alias = "require" nl* "(" list-sep? require-fact-list? list-sep? ")"; +require = require-statement | require-statement-group; +export = "export" nl* definition; +simple-statement:alias = expression | send | go | defer | assignment | simple-statement-group; +simple-statement-group:alias = "(" nl* simple-statement nl* ")"; +statement:alias = simple-statement + | return + | if + | switch + | select + | loop + | definition + | require + | export + | statement-group; +statement-group:alias = "(" nl* statement nl* ")"; +sep:alias = (";" | nl)+; +statement-list:alias = statement (sep statement)*; +shebang-command = [^\n]*; +shebang = "#!" shebang-command "\n"; +mml:root = shebang? sep? statement-list? sep?; diff --git a/docs/examples/scheme.treerack b/doc/example/scheme.treerack similarity index 66% rename from docs/examples/scheme.treerack rename to doc/example/scheme.treerack index e07c888..e2ac7b6 100644 --- a/docs/examples/scheme.treerack +++ b/doc/example/scheme.treerack @@ -1,10 +1,9 @@ whitespace:ws = [ \b\f\n\r\t\v]; comment:ws = ";" [^\n]*; -number:nows = "-"? ("0" | [1-9][0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; +number:nows = "-"? ("0" | [1-9] [0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; string:nows = "\"" ([^\\"] | "\\" .)* "\""; symbol:nows = ([^\\ \n\t\b\f\r\v\"()\[\]#] | "\\" .)+; -list-form:alias = "(" expression* ")" - | "[" expression* "]"; +list-form:alias = "(" expression* ")" | "[" expression* "]"; list = list-form; vector = "#" list-form; expression:alias = number | string | symbol | list; diff --git a/docs/examples/sexpr.treerack b/doc/example/sexpr.treerack similarity index 78% rename from docs/examples/sexpr.treerack rename to doc/example/sexpr.treerack index 0bc95a6..9d2f961 100644 --- a/docs/examples/sexpr.treerack +++ b/doc/example/sexpr.treerack @@ -1,6 +1,6 @@ whitespace:ws = [ \b\f\n\r\t\v]; comment:ws = ";" [^\n]*; -number:nows = "-"? ("0" | [1-9][0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; +number:nows = "-"? ("0" | [1-9] [0-9]*) ("." [0-9]+)? ([eE] [+\-]? [0-9]+)?; string:nows = "\"" ([^\\"] | "\\" .)* "\""; symbol:nows = ([^\\ \n\t\b\f\r\v\"()] | "\\" .)+; list = "(" expression* ")"; diff --git a/docs/examples/test.mml b/doc/example/test.mml similarity index 100% rename from docs/examples/test.mml rename to doc/example/test.mml diff --git a/docs/manual.md b/doc/manual.md similarity index 98% rename from docs/manual.md rename to doc/manual.md index 7eaca96..2a208bc 100644 --- a/docs/manual.md +++ b/doc/manual.md @@ -531,7 +531,7 @@ $ go run . > exit ``` -We can find the source files for this example here: [./examples/acalc](./examples/acalc). +We can find the source files for this example here: [./example/acalc](./example/acalc). ## Important note: unescaping @@ -623,7 +623,7 @@ following workflow: - the detailed documentation of the treerack definition language: [./syntax.md](./syntax.md) - treerack command help: [../cmd/treerack/readme.md](../cmd/treerack/readme.md) or, if the command is installed, `man treerack`, or `path/to/treerack help` -- the arithmetic calculator example: [./examples/acalc](./examples/acalc). -- additional examples: [./examples](./examples) +- the arithmetic calculator example: [./example/acalc](./example/acalc). +- additional example: [./example](./example) Happy parsing! diff --git a/docs/syntax.md b/doc/syntax.md similarity index 98% rename from docs/syntax.md rename to doc/syntax.md index ad255c2..fb8592e 100644 --- a/docs/syntax.md +++ b/doc/syntax.md @@ -114,6 +114,6 @@ Comments follow C-style syntax and are ignored by the definition parser. ## Examples -- [JSON](examples/json.treerack) -- [Scheme](examples/scheme.treerack) +- [JSON](example/json.treerack) +- [Scheme](example/scheme.treerack) - [Treerack (itself)](../syntax.treerack) diff --git a/docs/examples/keyval.treerack b/docs/examples/keyval.treerack deleted file mode 100644 index 496f144..0000000 --- a/docs/examples/keyval.treerack +++ /dev/null @@ -1,20 +0,0 @@ -whitespace:ws = [ \b\f\r\t\v]; - -comment-line:alias = "#" [^\n]*; -comment = comment-line ("\n" comment-line)*; - -quoted:alias:nows = "\"" ([^\\"] | "\\" .)* "\""; -word:alias:nows = ([^\\"\n=#.\[\] \b\f\r\t\v] | "\\" .)+; -symbol = word+ | quoted; - -key-form:alias = symbol ("." symbol)*; -key = key-form; - -group-key = (comment "\n")? "[" key-form "]"; - -value-chars:alias:nows = ([^\\"\n=# \b\f\r\t\v] | "\\" .)+; -value = value-chars+ | quoted; - -key-val = (comment "\n")? (key | key? "=" value?) comment-line?; -entry:alias = group-key | key-val; -doc:root = (entry | comment-line | "\n")*; diff --git a/docs/examples/mml-exp.treerack b/docs/examples/mml-exp.treerack deleted file mode 100644 index 6016a94..0000000 --- a/docs/examples/mml-exp.treerack +++ /dev/null @@ -1,562 +0,0 @@ -// whitespace is ignored except for \n which is only ignored -// most of the time, but can serve as separator in: -// - list -// - struct -// - function args -// - statements -// - list, struct and function type constraints -// -// comments are not ignored because they are needed during formatting -ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; -wsc:ws = comment; -nl:alias = "\n"; - -// comments can be line or block comments -// indentation can hold meaning -line-comment-content:nows = [^\n]*; -line-comment:alias:nows = "//" line-comment-content; -block-comment-content:nows = ([^*] | "*" [^/])*; // TODO: why is the :nows required here if it is there for block-comment? -block-comment:alias:nows = "/*" block-comment-content "*/"; -comment-part:alias = line-comment | block-comment; -comment:alias = comment-part ("\n"? comment-part)*; - -decimal-digit:alias = [0-9]; -octal-digit:alias = [0-7]; -hexa-digit:alias = [0-9a-fA-F]; - -// interger examples: 42, 0666, 0xfff -decimal:alias:nows = [1-9] decimal-digit*; -octal:alias:nows = "0" octal-digit*; -hexa:alias:nows = "0" [xX] hexa-digit+; -int = decimal | octal | hexa; - -// float examples: .0, 0., 3.14, 1E-12 -exponent:alias:nows = [eE] [+\-]? decimal-digit+; -float:nows = decimal-digit+ "." decimal-digit* exponent? - | "." decimal-digit+ exponent? - | decimal-digit+ exponent; - -// string example: "Hello, world!" -// only \ and " need to be escaped, e.g. allows new lines -// common escaped chars get unescaped, the rest gets unescaped to themselves -string:nows = "\"" ([^\\"] | "\\" .)* "\""; - -true = "true"; -false = "false"; -bool:alias = true | false; - -// symbols normally can have only \w chars: fooBar_baz -// basic symbols cannot start with a digit -// some positions allow strings to be used as symbols, e.g: let "123" 123 -// when this is not possible, dynamic symbols need to be used, but they are -// not allowed in every case, e.g: {symbol(foo()): "bar"} -// TODO: needs decision log for dynamic symbol -// TODO: exclude keywords -// -// dynamic symbol decision log: -// - every value is equatable -// - structs can act as hashtables (optimization is transparent) -// - in structs, must differentiate between symbol and value of a symbol when used as a key -// - js style [a] would be enough for the structs -// - the variables in a scope are like fields in a struct -// - [a] would be ambigous with the list as an expression -// - a logical loophole is closed with symbol(a) -// - dynamic-symbols need to be handled differently in match expressions and type expressions -symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*; -static-symbol:alias = symbol | string; -dynamic-symbol = "symbol" "(" nl* expression nl* ")"; -symbol-expression:alias = static-symbol | dynamic-symbol; - -// TODO: what happens when a dynamic symbol gets exported? - -// list items are separated by comma or new line (or both) -/* - [] - [a, b, c] - [ - a - b - c - ] - [1, 2, a..., [b, c], [d, [e]]...] -*/ -spread-expression = primary-expression "..."; -list-sep:alias = ("," | "\n") (nl | ",")*; -list-item:alias = expression | spread-expression; -expression-list:alias = list-item (list-sep list-item)*; - -// list example: [1, 2, 3] -// lists can be constructed with other lists: [l1..., l2...] -list-fact:alias = "[" (nl | ",")* expression-list? (nl | ",")* "]"; -list = list-fact; -mutable-list = "~" nl* list-fact; - -indexer-symbol = "[" nl* expression nl* "]"; -entry = (symbol-expression | indexer-symbol) nl* ":" nl* expression; -entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; -struct-fact:alias = "{" (nl | ",")* entry-list? (nl | ",")* "}"; -struct = struct-fact; -mutable-struct = "~" nl* struct-fact; - -channel = "<>" | "<" nl* int nl* ">"; - -// and-expression:doc = "and" "(" (nl | ",")* expression-list? (nl | ",")* ")"; -// or-expression:doc = "or" "(" (nl | ",")* expression-list? (nl | ",")* ")"; - -argument-list:alias = static-symbol (list-sep static-symbol)*; -collect-symbol = "..." nl* static-symbol; -function-fact:alias = "(" (nl | ",")* - argument-list? - (nl | ",")* - collect-symbol? - (nl | ",")* ")" nl* - expression; -function = "fn" nl* function-fact; // can it ever cause a conflict with call and grouping? -effect = "fn" nl* "~" nl* function-fact; - -/* -a[42] -a[3:9] -a[:9] -a[3:] -a[b][c][d] -a.foo -a."foo" -a.symbol(foo) -*/ -range-from = expression; -range-to = expression; -range-expression:alias = range-from? nl* ":" nl* range-to?; -indexer-expression:alias = expression | range-expression; -expression-indexer:alias = primary-expression "[" nl* indexer-expression nl* "]"; -symbol-indexer:alias = primary-expression nl* "." nl* symbol-expression; -indexer = expression-indexer | symbol-indexer; - -function-application = primary-expression "(" (nl | ",")* expression-list? (nl | ",")* ")"; - -if = "if" nl* expression nl* block - (nl* "else" nl* "if" nl* expression nl* block)* - (nl* "else" nl* block)?; - -default = "default" nl* ":"; -default-line:alias = default (nl | ";")* statement?; -case = "case" nl* expression nl* ":"; -case-line:alias = case ";"* statement?; -switch = "switch" nl* expression? nl* "{" (nl | ";")* - ((case-line | default-line) (sep (case-line | default-line | statement))*)? - (nl | ";")* "}"; - -int-type = "int"; -float-type = "float"; -string-type = "string"; -bool-type = "bool"; -error-type = "error"; - -/* -support: - - go { - foo() - bar() - } - - go { for { f() } } - go func() { for { f() } }() - fn f() { go f() }; go f() - -and not: - - go for {foo()} - -or: - - go for foo() - -because we don't know what the arguments are -*/ - -/* - - fn foo() { - bar() - baz() - } - let qux foo() - -equivalent to: - - let qux { - bar() - baz() - } -*/ - -primitive-type:alias = int-type - | float-type - | string-type - | bool-type - | error-type; - -type-alias-name:alias = static-symbol; - -static-range-from = int; -static-range-to = int; -static-range-expression:alias = static-range-from? nl* ":" nl* static-range-to?; -items-quantifier = int | static-range-expression; -// TODO: maybe this can be confusing with matching constants. Shall we support matching constants, values? - -items-type = items-quantifier - | type-set (nl* ":" nl* items-quantifier)? - | static-symbol nl* type-set (nl* ":" nl* items-quantifier)?; - -destructure-item = type-set | static-symbol nl* type-set; - -collect-destructure-item = "..." nl* destructure-item? - (nl* ":" items-quantifier)?; -list-destructure-type = destructure-item - (list-sep destructure-item)* - (list-sep collect-destructure-item)? - | collect-destructure-item; -list-type-fact:alias = "[" (nl | ",")* - (items-type | list-destructure-type)? - (nl | ",")* "]"; -list-type = list-type-fact; -mutable-list-type = "~" nl* list-type-fact; - -destructure-match-item = match-set - | static-symbol nl* match-set - | static-symbol nl* static-symbol nl* match-set; - -collect-destructure-match-item = "..." nl* destructure-match-item? - (nl* ":" items-quantifier)?; -list-destructure-match = destructure-match-item - (list-sep destructure-match-item)* - (list-sep collect-destructure-match-item)? - | collect-destructure-match-item; -list-match-fact:alias = "[" (nl | ",")* - (list-destructure-match | items-type)? - (nl | ",")* "]"; -list-match = list-match-fact; -mutable-list-match = "~" nl* list-match; - -entry-type = static-symbol (nl* ":" nl* destructure-item)?; -entry-types:alias = entry-type (list-sep entry-type)*; -struct-type-fact:alias = "{" (nl | ",")* entry-types? (nl | ",")* "}"; -struct-type = struct-type-fact; -mutable-struct-type = "~" nl* struct-type-fact; - -entry-match = static-symbol (nl* ":" nl* destructure-match-item)?; -entry-matches:alias = entry-match (list-sep entry-match)*; -struct-match-fact:alias = "{" (nl | ",")* entry-matches? (nl | ",")* "}"; -struct-match = struct-match-fact; -mutable-struct-match = "~" nl* struct-match-fact; - -arg-type = type-set | static-symbol nl* type-set; -args-type:alias = arg-type (list-sep arg-type)*; -function-type-fact:alias = "(" nl* args-type? nl* ")" - (type-set | static-symbol type-set)?; -function-type = "fn" nl* function-type-fact; -effect-type = "fn" nl* "~" nl* function-type-fact; - -// TODO: heavy naming crime - -receive-direction = "receive"; -send-direction = "send"; -channel-type = "<" nl* - (receive-direction | send-direction)? nl* - destructure-item? - nl* ">"; - -type-fact-group:alias = "(" nl* type-fact nl* ")"; -type-fact:alias = primitive-type - | type-alias-name - | list-type - | mutable-list-type - | struct-type - | mutable-struct-type - | function-type - | effect-type - | channel-type - | type-fact-group; - -type-set:alias = type-fact (nl* "|" nl* type-fact)*; -type-expression:alias = type-set | static-symbol type-set; - -match-fact:alias = list-match - | mutable-list-match - | struct-match - | mutable-struct-match; - -match-set:alias = type-set | match-fact; -match-expression:alias = match-set | static-symbol match-set; - -match-case = "case" nl* match-expression nl* ":"; -match-case-line:alias = match-case ";"* statement?; -match = "match" nl* expression nl* "{" (nl | ";")* - ((match-case-line | default-line) - (sep (match-case-line | default-line | statement))*)? - (nl | ";")* "}"; - -conditional:alias = if - | switch - | match; - -receive-call = "receive" "(" (nl | ",")* expression (nl | ",")* ")"; -receive-op = "<-" primary-expression; -receive-expression-group:alias = "(" nl* receive-expression nl* ")"; -receive-expression:alias = receive-call | receive-op | receive-expression-group; - -receive-assign-capture:alias = assignable nl* ("=" nl*)? receive-expression; -receive-assignment = "set" nl* receive-assign-capture; -receive-assignment-equal = assignable nl* "=" nl* receive-expression; -receive-capture:alias = symbol-expression nl* ("=" nl*)? receive-expression; -receive-definition = "let" nl* receive-capture; -receive-mutable-definition = "let" nl* "~" nl* receive-capture; -receive-statement:alias = receive-assignment | receive-definition; - -send-call:alias = "send" "(" (nl | ",")* expression list-sep expression (nl | ",")* ")"; -send-op:alias = primary-expression "<-" expression; -send-call-group:alias = "(" nl* send nl* ")"; -send = send-call | send-op | send-call-group; - -close = "close" "(" (nl | ",")* expression (nl | ",")* ")"; - -communication-group:alias = "(" nl* communication nl* ")"; -communication:alias = receive-expression | receive-statement | send | communication-group; - -select-case = "case" nl* communication nl* ":"; -select-case-line:alias = select-case ";"* statement?; -select = "select" nl* "{" (nl | ";")* - ((select-case-line | default-line) - (sep (select-case-line | default-line | statement))*)? - (nl | ";")* "}"; - -go = "go" nl* (function-application | block); - -/* -require . = "mml/foo" -require bar = "mml/foo" -require . "mml/foo" -require bar "mml/foo" -require "mml/foo" -require ( - . = "mml/foo" - bar = "mml/foo" - . "mml/foo" - bar "mml/foo" - "mml/foo" -) -require () -*/ -require-inline = "."; -require-fact = string - | (static-symbol | require-inline) (nl* "=")? nl* string; -require-facts:alias = require-fact (list-sep require-fact)*; -require-statement:alias = "require" nl* require-fact; -require-statement-group:alias = "require" "(" (nl | ",")* - require-facts? - (nl | ",")* ")"; -require = require-statement | require-statement-group; - -panic = "panic" "(" (nl | ",")* expression (nl | ",")* ")"; -recover = "recover" "(" (nl | ",")* ")"; - -block = "{" (nl | ";")* statements? (nl | ";")* "}"; -expression-group:alias = "(" nl* expression nl* ")"; - -primary-expression:alias = int - | float - | string - | bool - | symbol - | dynamic-symbol - | list - | mutable-list - | struct - | mutable-struct - | channel - // | and-expression // only documentation - // | or-expression // only documentation - | function - | effect - | indexer - | function-application // pseudo-expression - | conditional // pseudo-expression - | receive-call - | select // pseudo-expression - | recover - | block // pseudo-expression - | expression-group; - -plus = "+"; -minus = "-"; -logical-not = "!"; -binary-not = "^"; -unary-operator:alias = plus | minus | logical-not | binary-not; -unary-expression = unary-operator primary-expression | receive-op; - -mul = "*"; -div = "/"; -mod = "%"; -lshift = "<<"; -rshift = ">>"; -binary-and = "&"; -and-not = "&^"; - -add = "+"; -sub = "-"; -binary-or = "|"; -xor = "^"; - -eq = "=="; -not-eq = "!="; -less = "<"; -less-or-eq = "<="; -greater = ">"; -greater-or-eq = ">="; - -logical-and = "&&"; -logical-or = "||"; - -chain = "->"; - -binary-op0:alias = mul | div | mod | lshift | rshift | binary-and | and-not; -binary-op1:alias = add | sub | binary-or | xor; -binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; -binary-op3:alias = logical-and; -binary-op4:alias = logical-or; -binary-op5:alias = chain; - -operand0:alias = primary-expression | unary-expression; -operand1:alias = operand0 | binary0; -operand2:alias = operand1 | binary1; -operand3:alias = operand2 | binary2; -operand4:alias = operand3 | binary3; -operand5:alias = operand4 | binary4; - -binary0 = operand0 (binary-op0 operand0)+; -binary1 = operand1 (binary-op1 operand1)+; -binary2 = operand2 (binary-op2 operand2)+; -binary3 = operand3 (binary-op3 operand3)+; -binary4 = operand4 (binary-op4 operand4)+; -binary5 = operand5 (binary-op5 operand5)+; - -binary-expression:alias = binary0 | binary1 | binary2 | binary3 | binary4 | binary5; - -ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; - -expression:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression; - -// TODO: code() -// TODO: observability - -break = "break"; -continue = "continue"; -loop-control:alias = break | continue; - -in-expression = static-symbol nl* "in" nl* (expression | range-expression); -loop-expression = expression | in-expression; -loop = "for" nl* (block | loop-expression nl* block); - -/* -a = b -set c = d -set e f -set ( - g = h - i j -) -*/ -assignable:alias = symbol-expression | indexer; -assign-capture = assignable nl* ("=" nl*)? expression; -assign-set:alias = "set" nl* assign-capture; -assign-equal = assignable nl* "=" nl* expression; -assign-captures:alias = assign-capture (list-sep assign-capture)*; -assign-group:alias = "set" nl* "(" (nl | ",")* assign-captures? (nl | ",")* ")"; -assignment = assign-set | assign-equal | assign-group; - -/* -let a = b -let c d -let ~ e = f -let ~ g h -let ( - i = j - k l - ~ m = n - ~ o p -) -let ~ ( - q = r - s t -) -*/ -value-capture-fact:alias = symbol-expression nl* ("=" nl*)? expression; -value-capture = value-capture-fact; -mutable-capture = "~" nl* value-capture-fact; -value-definition = "let" nl* (value-capture | mutable-capture); -value-captures:alias = value-capture (list-sep value-capture)*; -mixed-captures:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*; -value-definition-group = "let" nl* "(" (nl | ",")* mixed-captures? (nl | ",")* ")"; -mutable-definition-group = "let" nl* "~" nl* "(" (nl | ",")* value-captures? (nl | ",")* ")"; - -/* -fn a() b -fn ~ c() d -fn ( - e() f - ~ g() h -) -fn ~ ( - i() - j() -) -*/ -function-definition-fact:alias = static-symbol nl* function-fact; -function-capture = function-definition-fact; -effect-capture = "~" nl* function-definition-fact; -function-definition = "fn" nl* (function-capture | effect-capture); -function-captures:alias = function-capture (list-sep function-capture)*; -mixed-function-captures:alias = (function-capture | effect-capture) - (list-sep (function-capture | effect-capture))*; -function-definition-group = "fn" nl* "(" (nl | ",")* - mixed-function-captures? - (nl | ",")* ")"; -effect-definition-group = "fn" nl* "~" nl* "(" (nl | ",")* - function-captures? - (nl | ",")* ")"; - -definition:alias = value-definition - | value-definition-group - | mutable-definition-group - | function-definition - | function-definition-group - | effect-definition-group; - -type-alias = "type" nl* "alias" nl* static-symbol nl* type-set; -type-constraint = "type" nl* static-symbol nl* type-set; - -statement-group:alias = "(" nl* statement nl* ")"; - -statement:alias = send - | close - | panic - | require - | loop-control - | go - | loop - | assignment - | definition - | expression - | type-alias - | type-constraint - | statement-group; - -shebang-command = [^\n]*; -shebang = "#!" shebang-command "\n"; -sep:alias = (";" | "\n") (nl | ";")*; -statements:alias = statement (sep statement)*; -mml:root = shebang? (nl | ";")* statements? (nl | ";")*; diff --git a/docs/examples/mml-exp2.treerack b/docs/examples/mml-exp2.treerack deleted file mode 100644 index 4903efa..0000000 --- a/docs/examples/mml-exp2.treerack +++ /dev/null @@ -1,271 +0,0 @@ -ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; -wsc:ws = comment; -nl:alias = "\n"; - -line-comment-content:nows = [^\n]*; -line-comment:alias:nows = "//" line-comment-content; -block-comment-content:nows = ([^*] | "*" [^/])*; -block-comment:alias:nows = "/*" block-comment-content "*/"; -comment-part:alias = line-comment | block-comment; -comment:alias = comment-part (nl? comment-part)*; - -decimal-digit:alias = [0-9]; -octal-digit:alias = [0-7]; -hexa-digit:alias = [0-9a-fA-F]; - -decimal:alias:nows = [1-9] decimal-digit*; -octal:alias:nows = "0" octal-digit*; -hexa:alias:nows = "0" [xX] hexa-digit+; -int = decimal | octal | hexa; - -exponent:alias:nows = [eE] [+\-]? decimal-digit+; -float:nows = decimal-digit+ "." decimal-digit* exponent? - | "." decimal-digit+ exponent? - | decimal-digit+ exponent; - -string:nows = "\"" ([^\\"] | "\\" .)* "\""; - -true = "true"; -false = "false"; -bool:alias = true | false; - -symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*; - -spread-expression = primary-expression "..."; -list-sep:alias = (nl | ",")+; -list-item:alias = expression | spread-expression; -expression-list:alias = list-item (list-sep list-item)*; - -list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; -list = list-fact; -mutable-list = "~" nl* list-fact; - -expression-key = "[" nl* expression nl* "]"; -entry = (symbol | string | expression-key) nl* ":" nl* expression; -entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; -struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; -struct = struct-fact; -mutable-struct = "~" nl* struct-fact; - -parameter-list:alias = symbol (list-sep symbol)*; -collect-parameter = "..." nl* symbol; -return = "return" (nl* expression)?; -block = "{" sep? statement-list? sep? "}"; -function-fact:alias = "(" list-sep? - (parameter-list - | parameter-list list-sep collect-parameter - | collect-parameter)? - list-sep? ")" nl* - (simple-statement | block); -function = "fn" nl* function-fact; -effect = "fn" nl* "~" nl* function-fact; - -range-from = expression; -range-to = expression; -range:alias = range-from? nl* ":" nl* range-to?; - -symbol-index = "." nl* symbol; -expression-index = "[" nl* expression nl* "]"; -index:alias = symbol-index | expression-index; -index-list:alias = index (nl* index)?; -indexer = primary-expression nl* index-list; - -function-application = primary-expression "(" list-sep? expression-list? list-sep? ")"; - -expression-group:alias = "(" nl* expression nl* ")"; -primary-expression:alias = int - | float - | string - | bool - | receive - | symbol - | list - | mutable-list - | struct - | mutable-struct - | function - | effect - | indexer - | function-application - | expression-group; - -binary-not = "^"; -binary-and = "&"; -binary-or = "|"; -xor = "^"; -and-not = "&^"; -lshift = "<<"; -rshift = ">>"; - -plus = "+"; -minus = "-"; -mul = "*"; -div = "/"; -mod = "%"; -add = "+"; -sub = "-"; - -logical-not = "!"; -eq = "=="; -not-eq = "!="; -less = "<"; -less-or-eq = "<="; -greater = ">"; -greater-or-eq = ">="; -logical-and = "&&"; -logical-or = "||"; - -chain:alias = "->"; - -unary-operator:alias = plus | minus | binary-not | logical-not; -unary-expression = unary-operator primary-expression; - -binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; -binary-op1:alias = binary-or | xor | add | sub; -binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; -binary-op3:alias = logical-and; -binary-op4:alias = logical-or; - -operand0:alias = primary-expression | unary-expression; -operand1:alias = operand0 | binary0; -operand2:alias = operand1 | binary1; -operand3:alias = operand2 | binary2; -operand4:alias = operand3 | binary3; -operand5:alias = operand4 | binary4; - -binary0 = operand0 (nl* binary-op0 nl* operand0)+; -binary1 = operand1 (nl* binary-op1 nl* operand1)+; -binary2 = operand2 (nl* binary-op2 nl* operand2)+; -binary3 = operand3 (nl* binary-op3 nl* operand3)+; -binary4 = operand4 (nl* binary-op4 nl* operand4)+; - -binary-expression:alias = binary0 - | binary1 - | binary2 - | binary3 - | binary4; - -ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; - -chainingOperand:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression; -chaining = chainingOperand (nl* chain nl* chainingOperand)+; - -expression:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression - | chaining; - -if = "if" nl* expression nl* block - (nl* "else" nl* "if" nl* expression nl* block)* - (nl* "else" nl* block)?; - -// TODO: empty switch not parsed -default = "default" nl* ":"; -default-line:alias = default ";"* statement?; -case = "case" nl* expression nl* ":"; -case-line:alias = case ";"* statement?; -switch = "switch" nl* expression? nl* "{" sep? - ((case-line | default-line) (sep (case-line | default-line | statement))*)? - sep? "}"; - -send = "send" nl* primary-expression nl* primary-expression; -receive = "receive" nl* primary-expression; -receive-definition = symbol nl* receive; -communication:alias = send | receive | receive-definition; -select-case = "case" nl* communication nl* ":"; -select-case-line:alias = select-case ";"* statement?; -select = "select" nl* "{" sep? - ((select-case-line | default-line) - (sep (select-case-line | default-line | statement))*)? - sep? "}"; - -go = "go" nl* function-application; -defer = "defer" nl* function-application; - -range-over-expression = symbol nl* "in" nl* (expression | range) | range; -loop-expression:alias = expression | range-over-expression; -loop = "for" ((nl* loop-expression)? nl* block | nl* block); - -// TODO: set(a b) -assign-capture:alias = primary-expression (nl* "=")? nl* expression; -assign-capture-list:alias = assign-capture (list-sep assign-capture)*; -assign-set:alias = "set" nl* assign-capture; -assign-eq:alias = primary-expression nl* "=" nl* expression; -assign-group:alias = "set" nl* "(" (list-sep assign-capture-list)? list-sep? ")"; -assignment = assign-set | assign-eq | assign-group; - -value-capture-fact:alias = symbol (nl* "=")? nl* expression; -value-capture = value-capture-fact; -mutable-capture = "~" nl* value-capture-fact; -value-definition = "let" nl* (value-capture | mutable-capture); -mixed-capture-list:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*; -value-capture-list:alias = value-capture (list-sep value-capture)*; -value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; -mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; - -function-definition-fact:alias = symbol nl* function-fact; -function-capture = function-definition-fact; -effect-capture = "~" nl* function-definition-fact; -function-definition = "fn" nl* (function-capture | effect-capture); -function-capture-list:alias = function-capture (list-sep function-capture)*; -mixed-function-capture-list:alias = (function-capture | effect-capture) - (list-sep (function-capture | effect-capture))*; -function-definition-group = "fn" nl* "(" list-sep? - mixed-function-capture-list? - list-sep? ")"; -effect-definition-group = "fn" nl* "~" nl* "(" list-sep? - function-capture-list? - list-sep? ")"; - -definition:alias = value-definition - | value-definition-group - | mutable-definition-group - | function-definition - | function-definition-group - | effect-definition-group; - -// TODO: -// - use effect -// - rename to 'use' - -use-inline = "."; -use-fact = string - | (symbol | use-inline) (nl* "=")? nl* string; -use-fact-list:alias = use-fact (list-sep use-fact)*; -use-statement:alias = "use" nl* use-fact; -use-statement-group:alias = "use" nl* "(" list-sep? - use-fact-list? - list-sep? ")"; -use = use-statement | use-statement-group; - -export = "export" nl* definition; - -simple-statement:alias = send - | go - | defer - | assignment - | simple-statement-group - | expression; -simple-statement-group:alias = "(" nl* simple-statement nl* ")"; -statement:alias = return - | if - | switch - | select - | loop - | definition - | use - | export - | statement-group - | simple-statement; -statement-group:alias = "(" nl* statement nl* ")"; - -sep:alias = (";" | nl)+; -statement-list:alias = statement (sep statement)*; - -shebang-command = [^\n]*; -shebang = "#!" shebang-command "\n"; -mml:root = shebang? sep? statement-list? sep?; diff --git a/docs/examples/mml-exp3.treerack b/docs/examples/mml-exp3.treerack deleted file mode 100644 index dadd761..0000000 --- a/docs/examples/mml-exp3.treerack +++ /dev/null @@ -1,271 +0,0 @@ -ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; -wsc:ws = comment; -nl:alias = "\n"; -wsep:alias = ws | nl; - -line-comment-content:nows = [^\n]*; -line-comment:alias:nows = "//" line-comment-content; -block-comment-content:nows = ([^*] | "*" [^/])*; -block-comment:alias:nows = "/*" block-comment-content "*/"; -comment-part:alias = line-comment | block-comment; -comment:alias = comment-part (nl? comment-part)*; - -decimal-digit:alias = [0-9]; -octal-digit:alias = [0-7]; -hexa-digit:alias = [0-9a-fA-F]; - -decimal:alias:nows = [1-9] decimal-digit*; -octal:alias:nows = "0" octal-digit*; -hexa:alias:nows = "0" [xX] hexa-digit+; -int = decimal | octal | hexa; - -exponent:alias:nows = [eE] [+\-]? decimal-digit+; -float:nows = decimal-digit+ "." decimal-digit* exponent? - | "." decimal-digit+ exponent? - | decimal-digit+ exponent; - -string:nows = "\"" ([^\\"] | "\\" .)* "\""; - -true = "true"; -false = "false"; -bool:alias = true | false; - -symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*; - -spread-expression = primary-expression "..."; -list-sep:alias = (nl | ",")+; -list-item:alias = expression | spread-expression; -expression-list:alias = list-item (list-sep list-item)*; - -list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; -list = list-fact; -mutable-list = "~" nl* list-fact; - -expression-key = "[" nl* expression nl* "]"; -entry = (symbol | string | expression-key) nl* ":" nl* expression; -entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; -struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; -struct = struct-fact; -mutable-struct = "~" nl* struct-fact; - -parameter-list:alias = symbol (list-sep symbol)*; -collect-parameter = "..." nl* symbol; -return = "return" | "return" nl* expression; -block = "{" sep? statement-list? sep? "}"; -function-fact:alias = "(" list-sep? - (parameter-list - | parameter-list list-sep collect-parameter - | collect-parameter)? - list-sep? ")" nl* - (expression | simple-statement | block); -function = "fn" nl* function-fact; -effect = "fn" nl* "~" nl* function-fact; - -range-from = expression; -range-to = expression; -range:alias = range-from? nl* ":" nl* range-to?; - -symbol-index = "." nl* symbol; -expression-index = "[" nl* expression nl* "]"; -range-index = "[" nl* range nl* "]"; -index:alias = symbol-index | expression-index | range-index; -index-list:alias = index (nl* index)?; -indexer = primary-expression nl* index-list; - -application = primary-expression "(" list-sep? expression-list? list-sep? ")"; - -expression-group:alias = "(" nl* expression nl* ")"; -primary-expression:alias = int - | float - | string - | bool - | symbol - | list - | mutable-list - | struct - | mutable-struct - | function - | effect - | indexer - | application - | receive - | expression-group; - -binary-not = "^"; -binary-and = "&"; -binary-or = "|"; -xor = "^"; -and-not = "&^"; -lshift = "<<"; -rshift = ">>"; - -plus = "+"; -minus = "-"; -mul = "*"; -div = "/"; -mod = "%"; -add = "+"; -sub = "-"; - -logical-not = "!"; -eq = "=="; -not-eq = "!="; -less = "<"; -less-or-eq = "<="; -greater = ">"; -greater-or-eq = ">="; -logical-and = "&&"; -logical-or = "||"; - -chain:alias = "->"; - -unary-operator:alias = plus | minus | binary-not | logical-not; -unary-expression = unary-operator primary-expression; - -binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; -binary-op1:alias = binary-or | xor | add | sub; -binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; -binary-op3:alias = logical-and; -binary-op4:alias = logical-or; - -operand0:alias = primary-expression | unary-expression; -operand1:alias = operand0 | binary0; -operand2:alias = operand1 | binary1; -operand3:alias = operand2 | binary2; -operand4:alias = operand3 | binary3; -operand5:alias = operand4 | binary4; - -binary0 = operand0 (nl* binary-op0 nl* operand0)+; -binary1 = operand1 (nl* binary-op1 nl* operand1)+; -binary2 = operand2 (nl* binary-op2 nl* operand2)+; -binary3 = operand3 (nl* binary-op3 nl* operand3)+; -binary4 = operand4 (nl* binary-op4 nl* operand4)+; - -binary-expression:alias = binary0 - | binary1 - | binary2 - | binary3 - | binary4; - -ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; - -chainingOperand:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression; -chaining = chainingOperand (nl* chain nl* chainingOperand)+; - -expression:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression - | chaining; - -if = "if" nl* expression nl* block - (nl* "else" nl* "if" nl* expression nl* block)* - (nl* "else" nl* block)?; - -// TODO: empty switch not parsed -default = "default" nl* ":"; -default-line:alias = default ";"* statement?; -case = "case" nl* expression nl* ":"; -case-line:alias = case ";"* statement?; -switch = "switch" nl* expression? nl* "{" sep? - ((case-line | default-line) (sep (case-line | default-line | statement))*)? - sep? "}"; - -send = "send" nl* primary-expression nl* primary-expression; -receive = "receive" nl* primary-expression; -receive-definition = symbol nl* receive; -communication:alias = send | receive | receive-definition; -select-case = "case" nl* communication nl* ":"; -select-case-line:alias = select-case ";"* statement?; -select = "select" nl* "{" sep? - ((select-case-line | default-line) - (sep (select-case-line | default-line | statement))*)? - sep? "}"; - -go = "go" nl* application; -defer = "defer" nl* application; - -break = "break"; -continue = "continue"; -loop-control:alias = break | continue; -range-over = symbol nl* "in" nl* (expression | range) | range; -loop-expression:alias = expression | range-over; -loop = "for" ((nl* loop-expression)? nl* block | nl* block); - -assignment = (symbol | indexer) nl* "=" nl* expression; - -value-capture-fact:alias = symbol (nl* "=")? nl* expression; -value-capture = value-capture-fact; -mutable-capture = "~" nl* value-capture-fact; -value-definition = "let" nl* (value-capture | mutable-capture); -mixed-capture-list:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*; -value-capture-list:alias = value-capture (list-sep value-capture)*; -value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; -mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; - -function-definition-fact:alias = symbol nl* function-fact; -function-capture = function-definition-fact; -effect-capture = "~" nl* function-definition-fact; -function-definition = "fn" nl* (function-capture | effect-capture); -function-capture-list:alias = function-capture (list-sep function-capture)*; -mixed-function-capture-list:alias = (function-capture | effect-capture) - (list-sep (function-capture | effect-capture))*; -function-definition-group = "fn" nl* "(" list-sep? - mixed-function-capture-list? - list-sep? ")"; -effect-definition-group = "fn" nl* "~" nl* "(" list-sep? - function-capture-list? - list-sep? ")"; - -definition:alias = value-definition - | value-definition-group - | mutable-definition-group - | function-definition - | function-definition-group - | effect-definition-group; - -// TODO: -// - use effect - -use-inline = "."; -use-fact = string - | (symbol | use-inline) (nl* "=")? nl* string; -use-fact-list:alias = use-fact (list-sep use-fact)*; -use-statement:alias = "use" nl* use-fact; -use-statement-group:alias = "use" nl* "(" list-sep? - use-fact-list? - list-sep? ")"; -use = use-statement | use-statement-group; - -export = "export" nl* definition; - -simple-statement:alias = send - | go - | defer - | assignment - | simple-statement-group; -simple-statement-group:alias = "(" nl* simple-statement nl* ")"; -statement:alias = simple-statement - | return - | if - | switch - | select - | loop - | loop-control - | definition - | use - | export - | application // TODO: sendSomething() and sendSomething - | chaining - | statement-group; -statement-group:alias = "(" nl* statement nl* ")"; - -sep:alias = (";" | nl)+; -statement-list:alias = statement (sep statement)*; - -shebang-command = [^\n]*; -shebang = "#!" shebang-command "\n"; -mml:root = shebang? sep? statement-list? sep?; diff --git a/docs/examples/mml.treerack b/docs/examples/mml.treerack deleted file mode 100644 index 7d65e6d..0000000 --- a/docs/examples/mml.treerack +++ /dev/null @@ -1,266 +0,0 @@ -ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v"; -wsc:ws = comment; -nl:alias = "\n"; - -line-comment-content:nows = [^\n]*; -line-comment:alias:nows = "//" line-comment-content; -block-comment-content:nows = ([^*] | "*" [^/])*; -block-comment:alias:nows = "/*" block-comment-content "*/"; -comment-part:alias = line-comment | block-comment; -comment:alias = comment-part ("\n"? comment-part)*; - -decimal-digit:alias = [0-9]; -octal-digit:alias = [0-7]; -hexa-digit:alias = [0-9a-fA-F]; - -decimal:alias:nows = [1-9] decimal-digit*; -octal:alias:nows = "0" octal-digit*; -hexa:alias:nows = "0" [xX] hexa-digit+; -int = decimal | octal | hexa; - -exponent:alias:nows = [eE] [+\-]? decimal-digit+; -float:nows = decimal-digit+ "." decimal-digit* exponent? - | "." decimal-digit+ exponent? - | decimal-digit+ exponent; - -string:nows = "\"" ([^\\"] | "\\" .)* "\""; - -true = "true"; -false = "false"; -bool:alias = true | false; - -symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*; - -spread-expression = primary-expression "..."; -list-sep:alias = (nl | ",")+; -list-item:alias = expression | spread-expression; -expression-list:alias = list-item (list-sep list-item)*; - -list-fact:alias = "[" list-sep? expression-list? list-sep? "]"; -list = list-fact; -mutable-list = "~" nl* list-fact; - -expression-key = "[" nl* expression nl* "]"; -entry = (symbol | string | expression-key) nl* ":" nl* expression; -entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*; -struct-fact:alias = "{" list-sep? entry-list? list-sep? "}"; -struct = struct-fact; -mutable-struct = "~" nl* struct-fact; - -channel = "<>" | "<" nl* expression nl* ">"; - -parameter-list:alias = symbol (list-sep symbol)*; -collect-parameter = "..." nl* symbol; -return = "return" (nl* expression)?; -block = "{" sep? statement-list? sep? "}"; -function-fact:alias = "(" list-sep? - parameter-list? - (list-sep collect-parameter)? - list-sep? ")" nl* - (simple-statement | block); -function = "fn" nl* function-fact; -effect = "fn" nl* "~" nl* function-fact; - -range-from = expression; -range-to = expression; -range:alias = range-from? nl* ":" nl* range-to?; - -simple-indexer:alias = primary-expression "[" nl* expression nl* "]"; -range-indexer:alias = primary-expression "[" nl* range nl* "]"; -expression-indexer = simple-indexer | range-indexer; -symbol-indexer = primary-expression nl* "." nl* symbol; - -function-application = primary-expression "(" list-sep? expression-list? list-sep? ")"; - -expression-group:alias = "(" nl* expression nl* ")"; -primary-expression:alias = int - | float - | string - | bool - | symbol - | list - | mutable-list - | struct - | mutable-struct - | channel - | function - | effect - | expression-indexer - | symbol-indexer - | function-application - | receive - | expression-group; - -binary-not = "^"; -binary-and = "&"; -binary-or = "|"; -xor = "^"; -and-not = "&^"; -lshift = "<<"; -rshift = ">>"; - -plus = "+"; -minus = "-"; -mul = "*"; -div = "/"; -mod = "%"; -add = "+"; -sub = "-"; - -logical-not = "!"; -eq = "=="; -not-eq = "!="; -less = "<"; -less-or-eq = "<="; -greater = ">"; -greater-or-eq = ">="; -logical-and = "&&"; -logical-or = "||"; - -chain:alias = "->"; - -unary-operator:alias = plus | minus | binary-not | logical-not; -unary-expression = unary-operator primary-expression; - -binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod; -binary-op1:alias = binary-or | xor | add | sub; -binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq; -binary-op3:alias = logical-and; -binary-op4:alias = logical-or; - -operand0:alias = primary-expression | unary-expression; -operand1:alias = operand0 | binary0; -operand2:alias = operand1 | binary1; -operand3:alias = operand2 | binary2; -operand4:alias = operand3 | binary3; -operand5:alias = operand4 | binary4; - -binary0 = operand0 (binary-op0 operand0)+; -binary1 = operand1 (binary-op1 operand1)+; -binary2 = operand2 (binary-op2 operand2)+; -binary3 = operand3 (binary-op3 operand3)+; -binary4 = operand4 (binary-op4 operand4)+; -chaining = operand5 (nl* chain nl* operand5)+; - -binary-expression:alias = binary0 - | binary1 - | binary2 - | binary3 - | binary4 - | chaining; - -ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression; - -expression:alias = primary-expression - | unary-expression - | binary-expression - | ternary-expression; - -if = "if" nl* expression nl* block - (nl* "else" nl* "if" nl* expression nl* block)* - (nl* "else" nl* block)?; - -default = "default" nl* ":"; -default-line:alias = default ";"* statement?; -case = "case" nl* expression nl* ":"; -case-line:alias = case ";"* statement?; -switch = "switch" nl* expression? nl* "{" sep? - ((case-line | default-line) (sep (case-line | default-line | statement))*)? - sep? "}"; - -receive = "<<>" primary-expression; -receive-definition = "let" nl* symbol nl* receive; -receive-assignment = "set" nl* symbol nl* receive; -receive-statement:alias = receive-definition - | receive-assignment; -send = primary-expression "<<>" primary-expression; -communication:alias = receive | receive-statement | send; - -select-case = "case" nl* communication nl* ":"; -select-case-line:alias = select-case ";"* statement?; -select = "select" nl* "{" sep? - ((select-case-line | default-line) - (sep (select-case-line | default-line | statement))*)? - sep? "}"; - -go = "go" nl* function-application; -defer = "defer" nl* function-application; - -range-over-expression = symbol nl* "in" nl* (expression | range) | range; -loop-expression:alias = expression | range-over-expression; -loop = "for" ((nl* loop-expression)? nl* block | nl* block); - -assign-capture:alias = primary-expression (nl* "=")? nl* expression; -assign-capture-list:alias = assign-capture (list-sep assign-capture)*; -assign-set:alias = "set" nl* assign-capture; -assign-eq:alias = primary-expression nl* "=" nl* expression; -assign-group:alias = "set" nl* "(" (list-sep assign-capture-list)? list-sep? ")"; -assignment = assign-set | assign-eq | assign-group; - -value-capture-fact:alias = symbol (nl* "=")? nl* expression; -value-capture = value-capture-fact; -mutable-capture = "~" nl* value-capture-fact; -value-definition = "let" nl* (value-capture | mutable-capture); -mixed-capture-list:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*; -value-capture-list:alias = value-capture (list-sep value-capture)*; -value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")"; -mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")"; - -function-definition-fact:alias = symbol nl* function-fact; -function-capture = function-definition-fact; -effect-capture = "~" nl* function-definition-fact; -function-definition = "fn" nl* (function-capture | effect-capture); -function-capture-list:alias = function-capture (list-sep function-capture)*; -mixed-function-capture-list:alias = (function-capture | effect-capture) - (list-sep (function-capture | effect-capture))*; -function-definition-group = "fn" nl* "(" list-sep? - mixed-function-capture-list? - list-sep? ")"; -effect-definition-group = "fn" nl* "~" nl* "(" list-sep? - function-capture-list? - list-sep? ")"; - -definition:alias = value-definition - | value-definition-group - | mutable-definition-group - | function-definition - | function-definition-group - | effect-definition-group; - -require-inline = "."; -require-fact = string - | (symbol | require-inline) (nl* "=")? nl* string; -require-fact-list:alias = require-fact (list-sep require-fact)*; -require-statement:alias = "require" nl* require-fact; -require-statement-group:alias = "require" nl* "(" list-sep? - require-fact-list? - list-sep? ")"; -require = require-statement | require-statement-group; - -export = "export" nl* definition; - -simple-statement:alias = expression - | send - | go - | defer - | assignment - | simple-statement-group; -simple-statement-group:alias = "(" nl* simple-statement nl* ")"; -statement:alias = simple-statement - | return - | if - | switch - | select - | loop - | definition - | require - | export - | statement-group; -statement-group:alias = "(" nl* statement nl* ")"; - -sep:alias = (";" | nl)+; -statement-list:alias = statement (sep statement)*; - -shebang-command = [^\n]*; -shebang = "#!" shebang-command "\n"; -mml:root = shebang? sep? statement-list? sep?; diff --git a/errors_test.go b/errors_test.go index ecb050e..57fcd55 100644 --- a/errors_test.go +++ b/errors_test.go @@ -276,7 +276,7 @@ func TestErrorVerbose(t *testing.T) { // "c":3, // }` // - // s, err := openSyntaxFile("examples/json.treerack") + // s, err := openSyntaxFile("example/json.treerack") // if err != nil { // t.Error(err) // return diff --git a/format.go b/format.go index b943696..b3304f7 100644 --- a/format.go +++ b/format.go @@ -341,6 +341,7 @@ func formatSequenceItemNode(out io.Writer, targetWidth int, n *Node) error { } } + fprint("}") return err } @@ -659,6 +660,10 @@ func formatAST(out io.Writer, ast *Node) error { } } + if _, err := fmt.Fprintln(out); err != nil { + return err + } + return nil } diff --git a/format_test.go b/format_test.go index ec0ba36..8be6eac 100644 --- a/format_test.go +++ b/format_test.go @@ -396,7 +396,8 @@ flag:alias = alias | ws | nows | kw | nokw | failpass | root; definition-name:alias:nows = symbol (":" flag)*; definition = definition-name "=" expression; definitions:alias = definition (";"+ definition)*; -syntax:root = ";"* definitions? ";"*;` +syntax:root = ";"* definitions? ";"*; +` func TestDocFormat(t *testing.T) { for _, test := range []struct{ title, in, out string }{{ diff --git a/head.go b/head.go index f1d38fc..7ab216d 100644 --- a/head.go +++ b/head.go @@ -1,4 +1,4 @@ package treerack -// generated with scripts/createhead.go +// generated with script/createhead.go const headCode = "import (\n\t\"strconv\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"unicode\"\n\t\"fmt\"\n\t\"bufio\"\n)\n\ntype charParser struct {\n\tname\tstring\n\tid\tint\n\tnot\tbool\n\tchars\t[]rune\n\tranges\t[][]rune\n}\ntype charBuilder struct {\n\tname\tstring\n\tid\tint\n}\n\nfunc (p *charParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *charParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *charParser) commitType() commitType {\n\treturn alias\n}\nfunc matchChar(chars []rune, ranges [][]rune, not bool, char rune) bool {\n\tfor _, ci := range chars {\n\t\tif ci == char {\n\t\t\treturn !not\n\t\t}\n\t}\n\tfor _, ri := range ranges {\n\t\tif char >= ri[0] && char <= ri[1] {\n\t\t\treturn !not\n\t\t}\n\t}\n\treturn not\n}\nfunc (p *charParser) match(t rune) bool {\n\treturn matchChar(p.chars, p.ranges, p.not, t)\n}\nfunc (p *charParser) parse(c *context) {\n\tif tok, ok := c.token(); !ok || !p.match(tok) {\n\t\tif c.offset > c.failOffset {\n\t\t\tc.failOffset = c.offset\n\t\t\tc.failingParser = nil\n\t\t}\n\t\tc.fail(c.offset)\n\t\treturn\n\t}\n\tc.success(c.offset + 1)\n}\nfunc (b *charBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *charBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *charBuilder) build(c *context) ([]*node, bool) {\n\treturn nil, false\n}\n\ntype sequenceParser struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tcommitType\n\titems\t\t[]parser\n\tranges\t\t[][]int\n\tgeneralizations\t[]int\n\tallChars\tbool\n}\ntype sequenceBuilder struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tcommitType\n\titems\t\t[]builder\n\tranges\t\t[][]int\n\tgeneralizations\t[]int\n\tallChars\tbool\n}\n\nfunc (p *sequenceParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *sequenceParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *sequenceParser) commitType() commitType {\n\treturn p.commit\n}\nfunc (p *sequenceParser) parse(c *context) {\n\tif !p.allChars {\n\t\tif c.results.pending(c.offset, p.id) {\n\t\t\tc.fail(c.offset)\n\t\t\treturn\n\t\t}\n\t\tc.results.markPending(c.offset, p.id)\n\t}\n\tvar (\n\t\tcurrentCount\tint\n\t\tparsed\t\tbool\n\t)\n\titemIndex := 0\n\tfrom := c.offset\n\tto := c.offset\n\tfor itemIndex < len(p.items) {\n\t\tp.items[itemIndex].parse(c)\n\t\tif !c.matchLast {\n\t\t\tif currentCount >= p.ranges[itemIndex][0] {\n\t\t\t\titemIndex++\n\t\t\t\tcurrentCount = 0\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tc.offset = from\n\t\t\tif c.fromResults(p) {\n\t\t\t\tif to > c.failOffset {\n\t\t\t\t\tc.failOffset = -1\n\t\t\t\t\tc.failingParser = nil\n\t\t\t\t}\n\t\t\t\tif !p.allChars {\n\t\t\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&whitespace == 0 && p.commit&failPass == 0 {\n\t\t\t\tc.failingParser = p\n\t\t\t}\n\t\t\tc.fail(from)\n\t\t\tif !p.allChars {\n\t\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tparsed = c.offset > to\n\t\tif parsed {\n\t\t\tcurrentCount++\n\t\t}\n\t\tto = c.offset\n\t\tif !parsed || p.ranges[itemIndex][1] > 0 && currentCount == p.ranges[itemIndex][1] {\n\t\t\titemIndex++\n\t\t\tcurrentCount = 0\n\t\t}\n\t}\n\tif p.commit&noKeyword != 0 && c.isKeyword(from, to) {\n\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&whitespace == 0 && p.commit&failPass == 0 {\n\t\t\tc.failingParser = p\n\t\t}\n\t\tc.fail(from)\n\t\tif !p.allChars {\n\t\t\tc.results.unmarkPending(from, p.id)\n\t\t}\n\t\treturn\n\t}\n\tfor _, g := range p.generalizations {\n\t\tif c.results.pending(from, g) {\n\t\t\tc.results.setMatch(from, g, to)\n\t\t}\n\t}\n\tif to > c.failOffset {\n\t\tc.failOffset = -1\n\t\tc.failingParser = nil\n\t}\n\tc.results.setMatch(from, p.id, to)\n\tc.success(to)\n\tif !p.allChars {\n\t\tc.results.unmarkPending(from, p.id)\n\t}\n}\nfunc (b *sequenceBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *sequenceBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *sequenceBuilder) build(c *context) ([]*node, bool) {\n\tto, ok := c.results.longestMatch(c.offset, b.id)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tfrom := c.offset\n\tparsed := to > from\n\tif b.allChars {\n\t\tc.offset = to\n\t\tif b.commit&alias != 0 {\n\t\t\treturn nil, true\n\t\t}\n\t\treturn []*node{{Name: b.name, From: from, To: to, tokens: c.tokens}}, true\n\t} else if parsed {\n\t\tc.results.dropMatchTo(c.offset, b.id, to)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.dropMatchTo(c.offset, g, to)\n\t\t}\n\t} else {\n\t\tif c.results.pending(c.offset, b.id) {\n\t\t\treturn nil, false\n\t\t}\n\t\tc.results.markPending(c.offset, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.markPending(c.offset, g)\n\t\t}\n\t}\n\tvar (\n\t\titemIndex\tint\n\t\tcurrentCount\tint\n\t\tnodes\t\t[]*node\n\t)\n\tfor itemIndex < len(b.items) {\n\t\titemFrom := c.offset\n\t\tn, ok := b.items[itemIndex].build(c)\n\t\tif !ok {\n\t\t\titemIndex++\n\t\t\tcurrentCount = 0\n\t\t\tcontinue\n\t\t}\n\t\tif c.offset > itemFrom {\n\t\t\tnodes = append(nodes, n...)\n\t\t\tcurrentCount++\n\t\t\tif b.ranges[itemIndex][1] > 0 && currentCount == b.ranges[itemIndex][1] {\n\t\t\t\titemIndex++\n\t\t\t\tcurrentCount = 0\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif currentCount < b.ranges[itemIndex][0] {\n\t\t\tfor i := 0; i < b.ranges[itemIndex][0]-currentCount; i++ {\n\t\t\t\tnodes = append(nodes, n...)\n\t\t\t}\n\t\t}\n\t\titemIndex++\n\t\tcurrentCount = 0\n\t}\n\tif !parsed {\n\t\tc.results.unmarkPending(from, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.unmarkPending(from, g)\n\t\t}\n\t}\n\tif b.commit&alias != 0 {\n\t\treturn nodes, true\n\t}\n\treturn []*node{{Name: b.name, From: from, To: to, Nodes: nodes, tokens: c.tokens}}, true\n}\n\ntype choiceParser struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tcommitType\n\toptions\t\t[]parser\n\tgeneralizations\t[]int\n}\ntype choiceBuilder struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tcommitType\n\toptions\t\t[]builder\n\tgeneralizations\t[]int\n}\n\nfunc (p *choiceParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *choiceParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *choiceParser) commitType() commitType {\n\treturn p.commit\n}\nfunc (p *choiceParser) parse(c *context) {\n\tif c.fromResults(p) {\n\t\treturn\n\t}\n\tif c.results.pending(c.offset, p.id) {\n\t\tc.fail(c.offset)\n\t\treturn\n\t}\n\tc.results.markPending(c.offset, p.id)\n\tvar (\n\t\tmatch\t\tbool\n\t\toptionIndex\tint\n\t\tfoundMatch\tbool\n\t\tfailingParser\tparser\n\t)\n\tfrom := c.offset\n\tto := c.offset\n\tinitialFailOffset := c.failOffset\n\tinitialFailingParser := c.failingParser\n\tfailOffset := initialFailOffset\n\tfor {\n\t\tfoundMatch = false\n\t\toptionIndex = 0\n\t\tfor optionIndex < len(p.options) {\n\t\t\tp.options[optionIndex].parse(c)\n\t\t\toptionIndex++\n\t\t\tif !c.matchLast {\n\t\t\t\tif c.failOffset > failOffset {\n\t\t\t\t\tfailOffset = c.failOffset\n\t\t\t\t\tfailingParser = c.failingParser\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !c.matchLast || match && c.offset <= to {\n\t\t\t\tc.offset = from\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmatch = true\n\t\t\tfoundMatch = true\n\t\t\tto = c.offset\n\t\t\tc.offset = from\n\t\t\tc.results.setMatch(from, p.id, to)\n\t\t}\n\t\tif !foundMatch {\n\t\t\tbreak\n\t\t}\n\t}\n\tif match {\n\t\tif p.commit&noKeyword != 0 && c.isKeyword(from, to) {\n\t\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&whitespace == 0 && p.commit&failPass == 0 {\n\t\t\t\tc.failingParser = p\n\t\t\t}\n\t\t\tc.fail(from)\n\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\treturn\n\t\t}\n\t\tif failOffset > to {\n\t\t\tc.failOffset = failOffset\n\t\t\tc.failingParser = failingParser\n\t\t} else if to > initialFailOffset {\n\t\t\tc.failOffset = -1\n\t\t\tc.failingParser = nil\n\t\t} else {\n\t\t\tc.failOffset = initialFailOffset\n\t\t\tc.failingParser = initialFailingParser\n\t\t}\n\t\tc.success(to)\n\t\tc.results.unmarkPending(from, p.id)\n\t\treturn\n\t}\n\tif failOffset > initialFailOffset {\n\t\tc.failOffset = failOffset\n\t\tc.failingParser = failingParser\n\t\tif c.failingParser == nil && p.commitType()&userDefined != 0 && p.commitType()&whitespace == 0 && p.commitType()&failPass == 0 {\n\t\t\tc.failingParser = p\n\t\t}\n\t}\n\tc.results.setNoMatch(from, p.id)\n\tc.fail(from)\n\tc.results.unmarkPending(from, p.id)\n}\nfunc (b *choiceBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *choiceBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *choiceBuilder) build(c *context) ([]*node, bool) {\n\tto, ok := c.results.longestMatch(c.offset, b.id)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tfrom := c.offset\n\tparsed := to > from\n\tif parsed {\n\t\tc.results.dropMatchTo(c.offset, b.id, to)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.dropMatchTo(c.offset, g, to)\n\t\t}\n\t} else {\n\t\tif c.results.pending(c.offset, b.id) {\n\t\t\treturn nil, false\n\t\t}\n\t\tc.results.markPending(c.offset, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.markPending(c.offset, g)\n\t\t}\n\t}\n\tvar option builder\n\tfor _, o := range b.options {\n\t\tif c.results.hasMatchTo(c.offset, o.nodeID(), to) {\n\t\t\toption = o\n\t\t\tbreak\n\t\t}\n\t}\n\tn, _ := option.build(c)\n\tif !parsed {\n\t\tc.results.unmarkPending(from, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.unmarkPending(from, g)\n\t\t}\n\t}\n\tif b.commit&alias != 0 {\n\t\treturn n, true\n\t}\n\treturn []*node{{Name: b.name, From: from, To: to, Nodes: n, tokens: c.tokens}}, true\n}\n\ntype idSet struct{ ids []uint }\n\nfunc divModBits(id int) (int, int) {\n\treturn id / strconv.IntSize, id % strconv.IntSize\n}\nfunc (s *idSet) set(id int) {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\tif d < cap(s.ids) {\n\t\t\ts.ids = s.ids[:d+1]\n\t\t} else {\n\t\t\ts.ids = s.ids[:cap(s.ids)]\n\t\t\tfor i := cap(s.ids); i <= d; i++ {\n\t\t\t\ts.ids = append(s.ids, 0)\n\t\t\t}\n\t\t}\n\t}\n\ts.ids[d] |= 1 << uint(m)\n}\nfunc (s *idSet) unset(id int) {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\treturn\n\t}\n\ts.ids[d] &^= 1 << uint(m)\n}\nfunc (s *idSet) has(id int) bool {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\treturn false\n\t}\n\treturn s.ids[d]&(1< offset {\n\t\treturn ints\n\t}\n\tif cap(ints) > offset {\n\t\tints = ints[:offset+1]\n\t\treturn ints\n\t}\n\tints = ints[:cap(ints)]\n\tfor i := len(ints); i <= offset; i++ {\n\t\tints = append(ints, nil)\n\t}\n\treturn ints\n}\nfunc ensureOffsetIDs(ids []*idSet, offset int) []*idSet {\n\tif len(ids) > offset {\n\t\treturn ids\n\t}\n\tif cap(ids) > offset {\n\t\tids = ids[:offset+1]\n\t\treturn ids\n\t}\n\tids = ids[:cap(ids)]\n\tfor i := len(ids); i <= offset; i++ {\n\t\tids = append(ids, nil)\n\t}\n\treturn ids\n}\nfunc (r *results) setMatch(offset, id, to int) {\n\tr.match = ensureOffsetInts(r.match, offset)\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id || r.match[offset][i+1] != to {\n\t\t\tcontinue\n\t\t}\n\t\treturn\n\t}\n\tr.match[offset] = append(r.match[offset], id, to)\n}\nfunc (r *results) setNoMatch(offset, id int) {\n\tif len(r.match) > offset {\n\t\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\t\tif r.match[offset][i] != id {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tr.noMatch = ensureOffsetIDs(r.noMatch, offset)\n\tif r.noMatch[offset] == nil {\n\t\tr.noMatch[offset] = &idSet{}\n\t}\n\tr.noMatch[offset].set(id)\n}\nfunc (r *results) hasMatchTo(offset, id, to int) bool {\n\tif len(r.match) <= offset {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] == to {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (r *results) longestMatch(offset, id int) (int, bool) {\n\tif len(r.match) <= offset {\n\t\treturn 0, false\n\t}\n\tvar found bool\n\tto := -1\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] > to {\n\t\t\tto = r.match[offset][i+1]\n\t\t}\n\t\tfound = true\n\t}\n\treturn to, found\n}\nfunc (r *results) longestResult(offset, id int) (int, bool, bool) {\n\tif len(r.noMatch) > offset && r.noMatch[offset] != nil && r.noMatch[offset].has(id) {\n\t\treturn 0, false, true\n\t}\n\tto, ok := r.longestMatch(offset, id)\n\treturn to, ok, ok\n}\nfunc (r *results) dropMatchTo(offset, id, to int) {\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] == to {\n\t\t\tr.match[offset][i] = -1\n\t\t\treturn\n\t\t}\n\t}\n}\nfunc (r *results) resetPending() {\n\tr.isPending = nil\n}\nfunc (r *results) pending(offset, id int) bool {\n\tif len(r.isPending) <= id {\n\t\treturn false\n\t}\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == offset {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (r *results) markPending(offset, id int) {\n\tr.isPending = ensureOffsetInts(r.isPending, id)\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == -1 {\n\t\t\tr.isPending[id][i] = offset\n\t\t\treturn\n\t\t}\n\t}\n\tr.isPending[id] = append(r.isPending[id], offset)\n}\nfunc (r *results) unmarkPending(offset, id int) {\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == offset {\n\t\t\tr.isPending[id][i] = -1\n\t\t\tbreak\n\t\t}\n\t}\n}\n\ntype context struct {\n\treader\t\tio.RuneReader\n\tkeywords\t[]parser\n\toffset\t\tint\n\treadOffset\tint\n\tconsumed\tint\n\toffsetLimit\tint\n\tfailOffset\tint\n\tfailingParser\tparser\n\treadErr\t\terror\n\teof\t\tbool\n\tresults\t\t*results\n\ttokens\t\t[]rune\n\tmatchLast\tbool\n}\n\nfunc newContext(r io.RuneReader, keywords []parser) *context {\n\treturn &context{reader: r, keywords: keywords, results: &results{}, offsetLimit: -1, failOffset: -1}\n}\nfunc (c *context) read() bool {\n\tif c.eof || c.readErr != nil {\n\t\treturn false\n\t}\n\ttoken, n, err := c.reader.ReadRune()\n\tif err != nil {\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tif n == 0 {\n\t\t\t\tc.eof = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t} else {\n\t\t\tc.readErr = err\n\t\t\treturn false\n\t\t}\n\t}\n\tc.readOffset++\n\tif token == unicode.ReplacementChar {\n\t\tc.readErr = errInvalidUnicodeCharacter\n\t\treturn false\n\t}\n\tc.tokens = append(c.tokens, token)\n\treturn true\n}\nfunc (c *context) token() (rune, bool) {\n\tif c.offset == c.offsetLimit {\n\t\treturn 0, false\n\t}\n\tif c.offset == c.readOffset {\n\t\tif !c.read() {\n\t\t\treturn 0, false\n\t\t}\n\t}\n\treturn c.tokens[c.offset], true\n}\nfunc (c *context) fromResults(p parser) bool {\n\tto, m, ok := c.results.longestResult(c.offset, p.nodeID())\n\tif !ok {\n\t\treturn false\n\t}\n\tif m {\n\t\tc.success(to)\n\t} else {\n\t\tc.fail(c.offset)\n\t}\n\treturn true\n}\nfunc (c *context) isKeyword(from, to int) bool {\n\tol := c.offsetLimit\n\tc.offsetLimit = to\n\tdefer func() {\n\t\tc.offsetLimit = ol\n\t}()\n\tfor _, kw := range c.keywords {\n\t\tc.offset = from\n\t\tkw.parse(c)\n\t\tif c.matchLast && c.offset == to {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (c *context) success(to int) {\n\tc.offset = to\n\tc.matchLast = true\n\tif to > c.consumed {\n\t\tc.consumed = to\n\t}\n}\nfunc (c *context) fail(offset int) {\n\tc.offset = offset\n\tc.matchLast = false\n}\nfunc findLine(tokens []rune, offset int) (line, column int) {\n\tif offset < 0 {\n\t\treturn 0, 0\n\t}\n\ttokens = tokens[:offset]\n\tfor i := range tokens {\n\t\tcolumn++\n\t\tif tokens[i] == '\\n' {\n\t\t\tcolumn = 0\n\t\t\tline++\n\t\t}\n\t}\n\treturn\n}\nfunc (c *context) parseError(p parser) error {\n\tdefinition := p.nodeName()\n\tflagIndex := strings.Index(definition, \":\")\n\tif flagIndex > 0 {\n\t\tdefinition = definition[:flagIndex]\n\t}\n\tif c.failingParser == nil {\n\t\tc.failOffset = c.consumed\n\t}\n\tline, col := findLine(c.tokens, c.failOffset)\n\treturn &parseError{Offset: c.failOffset, Line: line, Column: col, Definition: definition}\n}\nfunc (c *context) finalizeParse(root parser) error {\n\tfp := c.failingParser\n\tif fp == nil {\n\t\tfp = root\n\t}\n\tto, match, found := c.results.longestResult(0, root.nodeID())\n\tif !found || !match || found && match && to < c.readOffset {\n\t\treturn c.parseError(fp)\n\t}\n\tc.read()\n\tif c.eof {\n\t\treturn nil\n\t}\n\tif c.readErr != nil {\n\t\treturn c.readErr\n\t}\n\treturn c.parseError(root)\n}\n\ntype node struct {\n\tName\tstring\n\tNodes\t[]*node\n\tFrom\tint\n\tTo\tint\n\ttokens\t[]rune\n}\n\nfunc (n *node) Tokens() []rune {\n\treturn n.tokens\n}\nfunc (n *node) String() string {\n\treturn fmt.Sprintf(\"%s:%d:%d:%s\", n.Name, n.From, n.To, n.Text())\n}\nfunc (n *node) Text() string {\n\treturn string(n.Tokens()[n.From:n.To])\n}\n\ntype commitType int\n\nconst (\n\tnone\tcommitType\t= 0\n\talias\tcommitType\t= 1 << iota\n\twhitespace\n\tnoWhitespace\n\tkeyword\n\tnoKeyword\n\tfailPass\n\troot\n\tuserDefined\n)\n\ntype formatFlags int\n\nconst (\n\tformatNone\tformatFlags\t= 0\n\tformatPretty\tformatFlags\t= 1 << iota\n\tformatIncludeComments\n)\n\ntype formatOptions struct {\n\tmode\t\tformatFlags\n\ttargetWidth\tint\n}\ntype parseError struct {\n\tInput\t\tstring\n\tOffset\t\tint\n\tLine\t\tint\n\tColumn\t\tint\n\tDefinition\tstring\n}\ntype parser interface {\n\tnodeName() string\n\tnodeID() int\n\tcommitType() commitType\n\tparse(*context)\n}\ntype builder interface {\n\tnodeName() string\n\tnodeID() int\n\tbuild(*context) ([]*node, bool)\n}\n\nvar errInvalidUnicodeCharacter = errors.New(\"invalid unicode character\")\n\nfunc (pe *parseError) Error() string {\n\treturn fmt.Sprintf(\"%s:%d:%d:parse failed, parsing: %s\", pe.Input, pe.Line+1, pe.Column+1, pe.Definition)\n}\nfunc parseInput(r io.Reader, p parser, b builder, kw []parser) (*node, error) {\n\tc := newContext(bufio.NewReader(r), kw)\n\tp.parse(c)\n\tif c.readErr != nil {\n\t\treturn nil, c.readErr\n\t}\n\tif err := c.finalizeParse(p); err != nil {\n\t\tif perr, ok := err.(*parseError); ok {\n\t\t\tperr.Input = \"\"\n\t\t}\n\t\treturn nil, err\n\t}\n\tc.offset = 0\n\tc.results.resetPending()\n\tn, _ := b.build(c)\n\treturn n[0], nil\n}\n" diff --git a/headexported.go b/headexported.go index 375a7af..a090798 100644 --- a/headexported.go +++ b/headexported.go @@ -1,4 +1,4 @@ package treerack -// generated with scripts/createhead.go +// generated with script/createhead.go const headCodeExported = "import (\n\t\"strconv\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"unicode\"\n\t\"fmt\"\n\t\"bufio\"\n)\n\ntype charParser struct {\n\tname\tstring\n\tid\tint\n\tnot\tbool\n\tchars\t[]rune\n\tranges\t[][]rune\n}\ntype charBuilder struct {\n\tname\tstring\n\tid\tint\n}\n\nfunc (p *charParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *charParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *charParser) commitType() CommitType {\n\treturn Alias\n}\nfunc matchChar(chars []rune, ranges [][]rune, not bool, char rune) bool {\n\tfor _, ci := range chars {\n\t\tif ci == char {\n\t\t\treturn !not\n\t\t}\n\t}\n\tfor _, ri := range ranges {\n\t\tif char >= ri[0] && char <= ri[1] {\n\t\t\treturn !not\n\t\t}\n\t}\n\treturn not\n}\nfunc (p *charParser) match(t rune) bool {\n\treturn matchChar(p.chars, p.ranges, p.not, t)\n}\nfunc (p *charParser) parse(c *context) {\n\tif tok, ok := c.token(); !ok || !p.match(tok) {\n\t\tif c.offset > c.failOffset {\n\t\t\tc.failOffset = c.offset\n\t\t\tc.failingParser = nil\n\t\t}\n\t\tc.fail(c.offset)\n\t\treturn\n\t}\n\tc.success(c.offset + 1)\n}\nfunc (b *charBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *charBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *charBuilder) build(c *context) ([]*Node, bool) {\n\treturn nil, false\n}\n\ntype sequenceParser struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tCommitType\n\titems\t\t[]parser\n\tranges\t\t[][]int\n\tgeneralizations\t[]int\n\tallChars\tbool\n}\ntype sequenceBuilder struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tCommitType\n\titems\t\t[]builder\n\tranges\t\t[][]int\n\tgeneralizations\t[]int\n\tallChars\tbool\n}\n\nfunc (p *sequenceParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *sequenceParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *sequenceParser) commitType() CommitType {\n\treturn p.commit\n}\nfunc (p *sequenceParser) parse(c *context) {\n\tif !p.allChars {\n\t\tif c.results.pending(c.offset, p.id) {\n\t\t\tc.fail(c.offset)\n\t\t\treturn\n\t\t}\n\t\tc.results.markPending(c.offset, p.id)\n\t}\n\tvar (\n\t\tcurrentCount\tint\n\t\tparsed\t\tbool\n\t)\n\titemIndex := 0\n\tfrom := c.offset\n\tto := c.offset\n\tfor itemIndex < len(p.items) {\n\t\tp.items[itemIndex].parse(c)\n\t\tif !c.matchLast {\n\t\t\tif currentCount >= p.ranges[itemIndex][0] {\n\t\t\t\titemIndex++\n\t\t\t\tcurrentCount = 0\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tc.offset = from\n\t\t\tif c.fromResults(p) {\n\t\t\t\tif to > c.failOffset {\n\t\t\t\t\tc.failOffset = -1\n\t\t\t\t\tc.failingParser = nil\n\t\t\t\t}\n\t\t\t\tif !p.allChars {\n\t\t\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {\n\t\t\t\tc.failingParser = p\n\t\t\t}\n\t\t\tc.fail(from)\n\t\t\tif !p.allChars {\n\t\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tparsed = c.offset > to\n\t\tif parsed {\n\t\t\tcurrentCount++\n\t\t}\n\t\tto = c.offset\n\t\tif !parsed || p.ranges[itemIndex][1] > 0 && currentCount == p.ranges[itemIndex][1] {\n\t\t\titemIndex++\n\t\t\tcurrentCount = 0\n\t\t}\n\t}\n\tif p.commit&NoKeyword != 0 && c.isKeyword(from, to) {\n\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {\n\t\t\tc.failingParser = p\n\t\t}\n\t\tc.fail(from)\n\t\tif !p.allChars {\n\t\t\tc.results.unmarkPending(from, p.id)\n\t\t}\n\t\treturn\n\t}\n\tfor _, g := range p.generalizations {\n\t\tif c.results.pending(from, g) {\n\t\t\tc.results.setMatch(from, g, to)\n\t\t}\n\t}\n\tif to > c.failOffset {\n\t\tc.failOffset = -1\n\t\tc.failingParser = nil\n\t}\n\tc.results.setMatch(from, p.id, to)\n\tc.success(to)\n\tif !p.allChars {\n\t\tc.results.unmarkPending(from, p.id)\n\t}\n}\nfunc (b *sequenceBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *sequenceBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *sequenceBuilder) build(c *context) ([]*Node, bool) {\n\tto, ok := c.results.longestMatch(c.offset, b.id)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tfrom := c.offset\n\tparsed := to > from\n\tif b.allChars {\n\t\tc.offset = to\n\t\tif b.commit&Alias != 0 {\n\t\t\treturn nil, true\n\t\t}\n\t\treturn []*Node{{Name: b.name, From: from, To: to, tokens: c.tokens}}, true\n\t} else if parsed {\n\t\tc.results.dropMatchTo(c.offset, b.id, to)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.dropMatchTo(c.offset, g, to)\n\t\t}\n\t} else {\n\t\tif c.results.pending(c.offset, b.id) {\n\t\t\treturn nil, false\n\t\t}\n\t\tc.results.markPending(c.offset, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.markPending(c.offset, g)\n\t\t}\n\t}\n\tvar (\n\t\titemIndex\tint\n\t\tcurrentCount\tint\n\t\tnodes\t\t[]*Node\n\t)\n\tfor itemIndex < len(b.items) {\n\t\titemFrom := c.offset\n\t\tn, ok := b.items[itemIndex].build(c)\n\t\tif !ok {\n\t\t\titemIndex++\n\t\t\tcurrentCount = 0\n\t\t\tcontinue\n\t\t}\n\t\tif c.offset > itemFrom {\n\t\t\tnodes = append(nodes, n...)\n\t\t\tcurrentCount++\n\t\t\tif b.ranges[itemIndex][1] > 0 && currentCount == b.ranges[itemIndex][1] {\n\t\t\t\titemIndex++\n\t\t\t\tcurrentCount = 0\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif currentCount < b.ranges[itemIndex][0] {\n\t\t\tfor i := 0; i < b.ranges[itemIndex][0]-currentCount; i++ {\n\t\t\t\tnodes = append(nodes, n...)\n\t\t\t}\n\t\t}\n\t\titemIndex++\n\t\tcurrentCount = 0\n\t}\n\tif !parsed {\n\t\tc.results.unmarkPending(from, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.unmarkPending(from, g)\n\t\t}\n\t}\n\tif b.commit&Alias != 0 {\n\t\treturn nodes, true\n\t}\n\treturn []*Node{{Name: b.name, From: from, To: to, Nodes: nodes, tokens: c.tokens}}, true\n}\n\ntype choiceParser struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tCommitType\n\toptions\t\t[]parser\n\tgeneralizations\t[]int\n}\ntype choiceBuilder struct {\n\tname\t\tstring\n\tid\t\tint\n\tcommit\t\tCommitType\n\toptions\t\t[]builder\n\tgeneralizations\t[]int\n}\n\nfunc (p *choiceParser) nodeName() string {\n\treturn p.name\n}\nfunc (p *choiceParser) nodeID() int {\n\treturn p.id\n}\nfunc (p *choiceParser) commitType() CommitType {\n\treturn p.commit\n}\nfunc (p *choiceParser) parse(c *context) {\n\tif c.fromResults(p) {\n\t\treturn\n\t}\n\tif c.results.pending(c.offset, p.id) {\n\t\tc.fail(c.offset)\n\t\treturn\n\t}\n\tc.results.markPending(c.offset, p.id)\n\tvar (\n\t\tmatch\t\tbool\n\t\toptionIndex\tint\n\t\tfoundMatch\tbool\n\t\tfailingParser\tparser\n\t)\n\tfrom := c.offset\n\tto := c.offset\n\tinitialFailOffset := c.failOffset\n\tinitialFailingParser := c.failingParser\n\tfailOffset := initialFailOffset\n\tfor {\n\t\tfoundMatch = false\n\t\toptionIndex = 0\n\t\tfor optionIndex < len(p.options) {\n\t\t\tp.options[optionIndex].parse(c)\n\t\t\toptionIndex++\n\t\t\tif !c.matchLast {\n\t\t\t\tif c.failOffset > failOffset {\n\t\t\t\t\tfailOffset = c.failOffset\n\t\t\t\t\tfailingParser = c.failingParser\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !c.matchLast || match && c.offset <= to {\n\t\t\t\tc.offset = from\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmatch = true\n\t\t\tfoundMatch = true\n\t\t\tto = c.offset\n\t\t\tc.offset = from\n\t\t\tc.results.setMatch(from, p.id, to)\n\t\t}\n\t\tif !foundMatch {\n\t\t\tbreak\n\t\t}\n\t}\n\tif match {\n\t\tif p.commit&NoKeyword != 0 && c.isKeyword(from, to) {\n\t\t\tif c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {\n\t\t\t\tc.failingParser = p\n\t\t\t}\n\t\t\tc.fail(from)\n\t\t\tc.results.unmarkPending(from, p.id)\n\t\t\treturn\n\t\t}\n\t\tif failOffset > to {\n\t\t\tc.failOffset = failOffset\n\t\t\tc.failingParser = failingParser\n\t\t} else if to > initialFailOffset {\n\t\t\tc.failOffset = -1\n\t\t\tc.failingParser = nil\n\t\t} else {\n\t\t\tc.failOffset = initialFailOffset\n\t\t\tc.failingParser = initialFailingParser\n\t\t}\n\t\tc.success(to)\n\t\tc.results.unmarkPending(from, p.id)\n\t\treturn\n\t}\n\tif failOffset > initialFailOffset {\n\t\tc.failOffset = failOffset\n\t\tc.failingParser = failingParser\n\t\tif c.failingParser == nil && p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 && p.commitType()&FailPass == 0 {\n\t\t\tc.failingParser = p\n\t\t}\n\t}\n\tc.results.setNoMatch(from, p.id)\n\tc.fail(from)\n\tc.results.unmarkPending(from, p.id)\n}\nfunc (b *choiceBuilder) nodeName() string {\n\treturn b.name\n}\nfunc (b *choiceBuilder) nodeID() int {\n\treturn b.id\n}\nfunc (b *choiceBuilder) build(c *context) ([]*Node, bool) {\n\tto, ok := c.results.longestMatch(c.offset, b.id)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tfrom := c.offset\n\tparsed := to > from\n\tif parsed {\n\t\tc.results.dropMatchTo(c.offset, b.id, to)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.dropMatchTo(c.offset, g, to)\n\t\t}\n\t} else {\n\t\tif c.results.pending(c.offset, b.id) {\n\t\t\treturn nil, false\n\t\t}\n\t\tc.results.markPending(c.offset, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.markPending(c.offset, g)\n\t\t}\n\t}\n\tvar option builder\n\tfor _, o := range b.options {\n\t\tif c.results.hasMatchTo(c.offset, o.nodeID(), to) {\n\t\t\toption = o\n\t\t\tbreak\n\t\t}\n\t}\n\tn, _ := option.build(c)\n\tif !parsed {\n\t\tc.results.unmarkPending(from, b.id)\n\t\tfor _, g := range b.generalizations {\n\t\t\tc.results.unmarkPending(from, g)\n\t\t}\n\t}\n\tif b.commit&Alias != 0 {\n\t\treturn n, true\n\t}\n\treturn []*Node{{Name: b.name, From: from, To: to, Nodes: n, tokens: c.tokens}}, true\n}\n\ntype idSet struct{ ids []uint }\n\nfunc divModBits(id int) (int, int) {\n\treturn id / strconv.IntSize, id % strconv.IntSize\n}\nfunc (s *idSet) set(id int) {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\tif d < cap(s.ids) {\n\t\t\ts.ids = s.ids[:d+1]\n\t\t} else {\n\t\t\ts.ids = s.ids[:cap(s.ids)]\n\t\t\tfor i := cap(s.ids); i <= d; i++ {\n\t\t\t\ts.ids = append(s.ids, 0)\n\t\t\t}\n\t\t}\n\t}\n\ts.ids[d] |= 1 << uint(m)\n}\nfunc (s *idSet) unset(id int) {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\treturn\n\t}\n\ts.ids[d] &^= 1 << uint(m)\n}\nfunc (s *idSet) has(id int) bool {\n\td, m := divModBits(id)\n\tif d >= len(s.ids) {\n\t\treturn false\n\t}\n\treturn s.ids[d]&(1< offset {\n\t\treturn ints\n\t}\n\tif cap(ints) > offset {\n\t\tints = ints[:offset+1]\n\t\treturn ints\n\t}\n\tints = ints[:cap(ints)]\n\tfor i := len(ints); i <= offset; i++ {\n\t\tints = append(ints, nil)\n\t}\n\treturn ints\n}\nfunc ensureOffsetIDs(ids []*idSet, offset int) []*idSet {\n\tif len(ids) > offset {\n\t\treturn ids\n\t}\n\tif cap(ids) > offset {\n\t\tids = ids[:offset+1]\n\t\treturn ids\n\t}\n\tids = ids[:cap(ids)]\n\tfor i := len(ids); i <= offset; i++ {\n\t\tids = append(ids, nil)\n\t}\n\treturn ids\n}\nfunc (r *results) setMatch(offset, id, to int) {\n\tr.match = ensureOffsetInts(r.match, offset)\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id || r.match[offset][i+1] != to {\n\t\t\tcontinue\n\t\t}\n\t\treturn\n\t}\n\tr.match[offset] = append(r.match[offset], id, to)\n}\nfunc (r *results) setNoMatch(offset, id int) {\n\tif len(r.match) > offset {\n\t\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\t\tif r.match[offset][i] != id {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tr.noMatch = ensureOffsetIDs(r.noMatch, offset)\n\tif r.noMatch[offset] == nil {\n\t\tr.noMatch[offset] = &idSet{}\n\t}\n\tr.noMatch[offset].set(id)\n}\nfunc (r *results) hasMatchTo(offset, id, to int) bool {\n\tif len(r.match) <= offset {\n\t\treturn false\n\t}\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] == to {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (r *results) longestMatch(offset, id int) (int, bool) {\n\tif len(r.match) <= offset {\n\t\treturn 0, false\n\t}\n\tvar found bool\n\tto := -1\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] > to {\n\t\t\tto = r.match[offset][i+1]\n\t\t}\n\t\tfound = true\n\t}\n\treturn to, found\n}\nfunc (r *results) longestResult(offset, id int) (int, bool, bool) {\n\tif len(r.noMatch) > offset && r.noMatch[offset] != nil && r.noMatch[offset].has(id) {\n\t\treturn 0, false, true\n\t}\n\tto, ok := r.longestMatch(offset, id)\n\treturn to, ok, ok\n}\nfunc (r *results) dropMatchTo(offset, id, to int) {\n\tfor i := 0; i < len(r.match[offset]); i += 2 {\n\t\tif r.match[offset][i] != id {\n\t\t\tcontinue\n\t\t}\n\t\tif r.match[offset][i+1] == to {\n\t\t\tr.match[offset][i] = -1\n\t\t\treturn\n\t\t}\n\t}\n}\nfunc (r *results) resetPending() {\n\tr.isPending = nil\n}\nfunc (r *results) pending(offset, id int) bool {\n\tif len(r.isPending) <= id {\n\t\treturn false\n\t}\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == offset {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (r *results) markPending(offset, id int) {\n\tr.isPending = ensureOffsetInts(r.isPending, id)\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == -1 {\n\t\t\tr.isPending[id][i] = offset\n\t\t\treturn\n\t\t}\n\t}\n\tr.isPending[id] = append(r.isPending[id], offset)\n}\nfunc (r *results) unmarkPending(offset, id int) {\n\tfor i := range r.isPending[id] {\n\t\tif r.isPending[id][i] == offset {\n\t\t\tr.isPending[id][i] = -1\n\t\t\tbreak\n\t\t}\n\t}\n}\n\ntype context struct {\n\treader\t\tio.RuneReader\n\tkeywords\t[]parser\n\toffset\t\tint\n\treadOffset\tint\n\tconsumed\tint\n\toffsetLimit\tint\n\tfailOffset\tint\n\tfailingParser\tparser\n\treadErr\t\terror\n\teof\t\tbool\n\tresults\t\t*results\n\ttokens\t\t[]rune\n\tmatchLast\tbool\n}\n\nfunc newContext(r io.RuneReader, keywords []parser) *context {\n\treturn &context{reader: r, keywords: keywords, results: &results{}, offsetLimit: -1, failOffset: -1}\n}\nfunc (c *context) read() bool {\n\tif c.eof || c.readErr != nil {\n\t\treturn false\n\t}\n\ttoken, n, err := c.reader.ReadRune()\n\tif err != nil {\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tif n == 0 {\n\t\t\t\tc.eof = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t} else {\n\t\t\tc.readErr = err\n\t\t\treturn false\n\t\t}\n\t}\n\tc.readOffset++\n\tif token == unicode.ReplacementChar {\n\t\tc.readErr = ErrInvalidUnicodeCharacter\n\t\treturn false\n\t}\n\tc.tokens = append(c.tokens, token)\n\treturn true\n}\nfunc (c *context) token() (rune, bool) {\n\tif c.offset == c.offsetLimit {\n\t\treturn 0, false\n\t}\n\tif c.offset == c.readOffset {\n\t\tif !c.read() {\n\t\t\treturn 0, false\n\t\t}\n\t}\n\treturn c.tokens[c.offset], true\n}\nfunc (c *context) fromResults(p parser) bool {\n\tto, m, ok := c.results.longestResult(c.offset, p.nodeID())\n\tif !ok {\n\t\treturn false\n\t}\n\tif m {\n\t\tc.success(to)\n\t} else {\n\t\tc.fail(c.offset)\n\t}\n\treturn true\n}\nfunc (c *context) isKeyword(from, to int) bool {\n\tol := c.offsetLimit\n\tc.offsetLimit = to\n\tdefer func() {\n\t\tc.offsetLimit = ol\n\t}()\n\tfor _, kw := range c.keywords {\n\t\tc.offset = from\n\t\tkw.parse(c)\n\t\tif c.matchLast && c.offset == to {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (c *context) success(to int) {\n\tc.offset = to\n\tc.matchLast = true\n\tif to > c.consumed {\n\t\tc.consumed = to\n\t}\n}\nfunc (c *context) fail(offset int) {\n\tc.offset = offset\n\tc.matchLast = false\n}\nfunc findLine(tokens []rune, offset int) (line, column int) {\n\tif offset < 0 {\n\t\treturn 0, 0\n\t}\n\ttokens = tokens[:offset]\n\tfor i := range tokens {\n\t\tcolumn++\n\t\tif tokens[i] == '\\n' {\n\t\t\tcolumn = 0\n\t\t\tline++\n\t\t}\n\t}\n\treturn\n}\nfunc (c *context) parseError(p parser) error {\n\tdefinition := p.nodeName()\n\tflagIndex := strings.Index(definition, \":\")\n\tif flagIndex > 0 {\n\t\tdefinition = definition[:flagIndex]\n\t}\n\tif c.failingParser == nil {\n\t\tc.failOffset = c.consumed\n\t}\n\tline, col := findLine(c.tokens, c.failOffset)\n\treturn &ParseError{Offset: c.failOffset, Line: line, Column: col, Definition: definition}\n}\nfunc (c *context) finalizeParse(root parser) error {\n\tfp := c.failingParser\n\tif fp == nil {\n\t\tfp = root\n\t}\n\tto, match, found := c.results.longestResult(0, root.nodeID())\n\tif !found || !match || found && match && to < c.readOffset {\n\t\treturn c.parseError(fp)\n\t}\n\tc.read()\n\tif c.eof {\n\t\treturn nil\n\t}\n\tif c.readErr != nil {\n\t\treturn c.readErr\n\t}\n\treturn c.parseError(root)\n}\n\ntype Node struct {\n\tName\tstring\n\tNodes\t[]*Node\n\tFrom\tint\n\tTo\tint\n\ttokens\t[]rune\n}\n\nfunc (n *Node) Tokens() []rune {\n\treturn n.tokens\n}\nfunc (n *Node) String() string {\n\treturn fmt.Sprintf(\"%s:%d:%d:%s\", n.Name, n.From, n.To, n.Text())\n}\nfunc (n *Node) Text() string {\n\treturn string(n.Tokens()[n.From:n.To])\n}\n\ntype CommitType int\n\nconst (\n\tNone\tCommitType\t= 0\n\tAlias\tCommitType\t= 1 << iota\n\tWhitespace\n\tNoWhitespace\n\tKeyword\n\tNoKeyword\n\tFailPass\n\tRoot\n\tuserDefined\n)\n\ntype formatFlags int\n\nconst (\n\tformatNone\tformatFlags\t= 0\n\tformatPretty\tformatFlags\t= 1 << iota\n\tformatIncludeComments\n)\n\ntype formatOptions struct {\n\tmode\t\tformatFlags\n\ttargetWidth\tint\n}\ntype ParseError struct {\n\tInput\t\tstring\n\tOffset\t\tint\n\tLine\t\tint\n\tColumn\t\tint\n\tDefinition\tstring\n}\ntype parser interface {\n\tnodeName() string\n\tnodeID() int\n\tcommitType() CommitType\n\tparse(*context)\n}\ntype builder interface {\n\tnodeName() string\n\tnodeID() int\n\tbuild(*context) ([]*Node, bool)\n}\n\nvar ErrInvalidUnicodeCharacter = errors.New(\"invalid unicode character\")\n\nfunc (pe *ParseError) Error() string {\n\treturn fmt.Sprintf(\"%s:%d:%d:parse failed, parsing: %s\", pe.Input, pe.Line+1, pe.Column+1, pe.Definition)\n}\nfunc parseInput(r io.Reader, p parser, b builder, kw []parser) (*Node, error) {\n\tc := newContext(bufio.NewReader(r), kw)\n\tp.parse(c)\n\tif c.readErr != nil {\n\t\treturn nil, c.readErr\n\t}\n\tif err := c.finalizeParse(p); err != nil {\n\t\tif perr, ok := err.(*ParseError); ok {\n\t\t\tperr.Input = \"\"\n\t\t}\n\t\treturn nil, err\n\t}\n\tc.offset = 0\n\tc.results.resetPending()\n\tn, _ := b.build(c)\n\treturn n[0], nil\n}\n" diff --git a/json_test.go b/json_test.go index e4c57d7..8376e32 100644 --- a/json_test.go +++ b/json_test.go @@ -285,7 +285,7 @@ func jsonTreeToJSON(n *Node) (interface{}, error) { } func TestJSON(t *testing.T) { - runTestsFile(t, "docs/examples/json.treerack", []testItem{{ + runTestsFile(t, "doc/example/json.treerack", []testItem{{ title: "true", text: "true", node: &Node{ @@ -509,7 +509,7 @@ func TestRandomJSON(t *testing.T) { buf := bytes.NewBuffer(b) - s, err := openSyntaxFile("docs/examples/json.treerack") + s, err := openSyntaxFile("doc/example/json.treerack") if err != nil { t.Error(err) return diff --git a/keyval_test.go b/keyval_test.go index 1f44e84..368d1b1 100644 --- a/keyval_test.go +++ b/keyval_test.go @@ -3,7 +3,7 @@ package treerack import "testing" func TestKeyVal(t *testing.T) { - runTestsFile(t, "docs/examples/keyval.treerack", []testItem{{ + runTestsFile(t, "doc/example/keyval.treerack", []testItem{{ title: "empty", }, { title: "a comment", diff --git a/mml_test.go b/mml_test.go index 5b873c3..b9c9874 100644 --- a/mml_test.go +++ b/mml_test.go @@ -3,7 +3,7 @@ package treerack import "testing" func TestMML(t *testing.T) { - s, err := openSyntaxFile("docs/examples/mml.treerack") + s, err := openSyntaxFile("doc/example/mml.treerack") if err != nil { t.Error(err) return diff --git a/mmlexp2_test.go b/mmlexp2_test.go index 7b3a255..73e8f6f 100644 --- a/mmlexp2_test.go +++ b/mmlexp2_test.go @@ -5,7 +5,7 @@ import ( ) func TestMMLExp2(t *testing.T) { - s, err := openSyntaxFile("docs/examples/mml-exp2.treerack") + s, err := openSyntaxFile("doc/example/mml-exp2.treerack") if err != nil { t.Error(err) return diff --git a/mmlexp3_test.go b/mmlexp3_test.go index e034318..887b6a2 100644 --- a/mmlexp3_test.go +++ b/mmlexp3_test.go @@ -5,7 +5,7 @@ import ( ) func TestMMLExp3(t *testing.T) { - s, err := openSyntaxFile("docs/examples/mml-exp3.treerack") + s, err := openSyntaxFile("doc/example/mml-exp3.treerack") if err != nil { t.Error(err) return diff --git a/mmlexp_test.go b/mmlexp_test.go index 5d766ef..bddd05d 100644 --- a/mmlexp_test.go +++ b/mmlexp_test.go @@ -9,7 +9,7 @@ import ( ) func TestMMLExp(t *testing.T) { - s, err := openSyntaxFile("docs/examples/mml-exp.treerack") + s, err := openSyntaxFile("doc/example/mml-exp.treerack") if err != nil { t.Error(err) return @@ -2987,7 +2987,7 @@ func TestMMLFile(t *testing.T) { const n = 180 - s, err := openSyntaxFile("docs/examples/mml-exp.treerack") + s, err := openSyntaxFile("doc/example/mml-exp.treerack") if err != nil { t.Error(err) return @@ -2995,7 +2995,7 @@ func TestMMLFile(t *testing.T) { s.Init() - f, err := os.Open("docs/examples/test.mml") + f, err := os.Open("doc/example/test.mml") if err != nil { t.Error(err) return diff --git a/readme.md b/readme.md index 905cd35..14d4d52 100644 --- a/readme.md +++ b/readme.md @@ -8,8 +8,8 @@ its Abstract Syntax Tree (AST) representation. It utilizes a custom syntax defin ## Examples -- **JSON**: [docs/examples/json.treerack](docs/examples/json.treerack) -- **Scheme**: [docs/examples/scheme.treerack](docs/examples/scheme.treerack) +- **JSON**: [doc/example/json.treerack](doc/example/json.treerack) +- **Scheme**: [doc/example/scheme.treerack](doc/example/scheme.treerack) - **Treerack (self-definition)**: [syntax.treerack](syntax.treerack) ## Overview @@ -42,8 +42,8 @@ go install code.squareroundforest.org/arpio/treerack/cmd/treerack ## Documentation -- [Manual](docs/manual.md): a guide to the main use cases supported by Treerack. -- [Syntax Definition](docs/syntax.md): detailed reference for the Treerack definition language. +- [Manual](doc/manual.md): a guide to the main use cases supported by Treerack. +- [Syntax Definition](doc/syntax.md): detailed reference for the Treerack definition language. - [Library Documentation](https://godocs.io/code.squareroundforest.org/arpio/treerack): GoDoc reference for the runtime library. diff --git a/scheme_test.go b/scheme_test.go index 4194126..4884e09 100644 --- a/scheme_test.go +++ b/scheme_test.go @@ -3,7 +3,7 @@ package treerack import "testing" func TestScheme(t *testing.T) { - runTestsFile(t, "docs/examples/scheme.treerack", []testItem{{ + runTestsFile(t, "doc/example/scheme.treerack", []testItem{{ title: "empty", }, { title: "a function", diff --git a/scripts/cmdreadme.go b/script/cmdreadme.go similarity index 100% rename from scripts/cmdreadme.go rename to script/cmdreadme.go diff --git a/scripts/createhead.go b/script/createhead.go similarity index 97% rename from scripts/createhead.go rename to script/createhead.go index 3d6094f..7c39f46 100644 --- a/scripts/createhead.go +++ b/script/createhead.go @@ -141,7 +141,7 @@ func main() { } fmt.Printf( - "package %s\n\n// generated with scripts/createhead.go\nconst %s=%s", + "package %s\n\n// generated with script/createhead.go\nconst %s=%s", packageName, varName, quotedCode, diff --git a/scripts/docreflect.go b/script/docreflect.go similarity index 100% rename from scripts/docreflect.go rename to script/docreflect.go diff --git a/script/format.go b/script/format.go new file mode 100644 index 0000000..65abd47 --- /dev/null +++ b/script/format.go @@ -0,0 +1,46 @@ +package main + +import ( + "bytes" + "code.squareroundforest.org/arpio/treerack" + "fmt" + "log" + "os" +) + +func format(name string) (err error) { + var inBytes []byte + if inBytes, err = os.ReadFile(name); err != nil { + return + } + + in := bytes.NewBuffer(inBytes) + s := new(treerack.Syntax) + if err = s.ReadSyntax(in); err != nil { + return + } + + out := bytes.NewBuffer(nil) + if err = s.Format(out); err != nil { + return + } + + if bytes.Equal(out.Bytes(), inBytes) { + return + } + + fmt.Fprintln(os.Stderr, name) + if err = os.WriteFile(name, out.Bytes(), 0644); err != nil { + return + } + + return +} + +func main() { + for _, f := range os.Args[1:] { + if err := format(f); err != nil { + log.Fatalf("%s: %v", f, err) + } + } +} diff --git a/scripts/man.go b/script/man.go similarity index 100% rename from scripts/man.go rename to script/man.go diff --git a/sexpr_test.go b/sexpr_test.go index c02f60e..d3b45b9 100644 --- a/sexpr_test.go +++ b/sexpr_test.go @@ -3,7 +3,7 @@ package treerack import "testing" func TestSExpr(t *testing.T) { - runTestsFile(t, "docs/examples/sexpr.treerack", []testItem{{ + runTestsFile(t, "doc/example/sexpr.treerack", []testItem{{ title: "number", text: "42", nodes: []*Node{{ diff --git a/syntax.go b/syntax.go index f401bd7..b0372f8 100644 --- a/syntax.go +++ b/syntax.go @@ -483,7 +483,7 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error { fprintln() fprintln() - // generate headCode with scripts/createhead.go + // generate headCode with script/createhead.go hc := headCode if o.Export { hc = headCodeExported diff --git a/syntax.treerack b/syntax.treerack index cc2df3f..717dde1 100644 --- a/syntax.treerack +++ b/syntax.treerack @@ -1,82 +1,84 @@ /* foo */ -// bar - // bar - //baz - /* foo - bar baz */// foo bar baz -wschar:alias =// foo - /* bar */ " " | "\t" | "\n" | "\b" | "\f" | "\r" | "\v"; -wsc:ws = wschar | comment; - -block-comment:alias:nows /* foo */ // bar -= "/*" ("*" [^/] | [^*])* "*/"; -line-comment:alias:nows /* -foo -*/ = "//" [^\n]*; +// bar +// bar +// baz +/* foo + bar baz */ // foo bar baz +wschar:alias = // foo + /* bar */ + " " | "\t" | "\n" | "\b" | "\f" | "\r" | "\v"; +wsc:ws = wschar | comment; +block-comment:alias:nows = /* foo */ // bar + "/*" ("*" [^/] | [^*])* "*/"; +line-comment:alias:nows = /* + foo + */ + "//" [^\n]*; comment-segment:alias:nows = // bar -line-comment | block-comment; -ws-no-nl:alias:nows = " " | "\t" | "\b" | /* this one */ /* is a */ "\f" /* form feed */ /* for sure */ | "\r" | "\v"; -comment:nows = comment-segment /* segment is not the best name */ /* but */ (ws-no-nl* "\n"? ws-no-nl* /* fine */ comment-segment)*; - -any-char = "."; // equivalent to [^] + line-comment | block-comment; +ws-no-nl:alias:nows = " " + | "\t" + | "\b" + /* this one */ /* is a */ + | "\f" + /* form feed */ /* for sure */ + | "\r" + | "\v"; +comment:nows = comment-segment + /* segment is not the best name */ /* but */ + ( ws-no-nl* + "\n"? + ws-no-nl* + /* fine */ + comment-segment + )*; +any-char = "."; // equivalent to [^] // caution: newline is accepted -/* class not */ class-not = "^"; -class-char:nows = [^\\\[\]\^\-] | "\\" . /* foo -bar */; -char-range:nows = class-char "-" class-char // foo -; +/* class not */ +class-not = "^"; +class-char:nows = [^\\\[\]\^\-] | "\\" .; /* foo + bar */ +char-range:nows = class-char "-" class-char; // foo char-class:nows = "[" class-not? (class-char | char-range)* "]"; // newline is accepted sequence-char:nows = [^\\"] | "\\" .; char-sequence:nows = "\"" sequence-char* "\""; - -terminal:alias = any-char | char-class | char-sequence; - -symbol:nows = [^\\ \n\t\b\f\r\v/.\[\]\"{}\^+*?|():=;]+; - -group:alias = "(" expression ")"; - -number:alias:nows = [0-9]+; -count = number; -count-quantifier = "{" count "}"; -range-from = number; -range-to = number; -range-quantifier = "{" range-from? "," range-to? "}"; -one-or-more = "+"; -zero-or-more = "*"; -zero-or-one = "?"; -quantity:alias = count-quantifier - | range-quantifier - | one-or-more - | zero-or-more - | zero-or-one; - -item:nows = (terminal | symbol | group) quantity?; -sequence = item+; - -option:alias = terminal | symbol | group | sequence; +terminal:alias = any-char | char-class | char-sequence; +symbol:nows = [^\\ \n\t\b\f\r\v/.\[\]\"{}\^+*?|():=;]+; +group:alias = "(" expression ")"; +number:alias:nows = [0-9]+; +count = number; +count-quantifier = "{" count "}"; +range-from = number; +range-to = number; +range-quantifier = "{" range-from? "," range-to? "}"; +one-or-more = "+"; +zero-or-more = "*"; +zero-or-one = "?"; +quantity:alias = count-quantifier | range-quantifier | one-or-more | zero-or-more | zero-or-one; +item:nows = (terminal | symbol | group) quantity?; +sequence = item+; +option:alias = terminal | symbol | group | sequence; // DOC: how the order matters choice = option ("|" option)+; // DOC: not having 'not' needs some tricks sometimes -expression:alias = terminal | symbol | group | sequence | choice; - -alias = "alias"; +expression:alias = terminal | symbol | group | sequence | choice; +alias = "alias"; ws = "ws"; -nows = "nows"; +nows = "nows"; kw = "kw"; nokw = "nokw"; -failpass = "failpass"; +failpass = "failpass"; root = "root"; -flag:alias = alias | ws | nows | kw | nokw | failpass | root; +flag:alias = alias | ws | nows | kw | nokw | failpass | root; definition-name:alias:nows = symbol (":" flag)*; -definition = definition-name "=" expression; - -definitions:alias = definition (";"+ definition)*; -syntax:root = ";"* definitions? ";"*; +definition = definition-name "=" expression; +definitions:alias = definition (";"+ definition)*; +syntax:root = ";"* definitions? ";"*;