1
0

formatter script for local syntax files

This commit is contained in:
Arpad Ryszka 2026-06-01 21:29:33 +02:00
parent b8036802f6
commit e025778538
43 changed files with 1362 additions and 1492 deletions

View File

@ -19,7 +19,7 @@ default: build
.build/head.go: $(sources) .build .build/head.go: $(sources) .build
go fmt ./... go fmt ./...
go run scripts/createhead.go -- \ go run script/createhead.go -- \
char.go \ char.go \
sequence.go \ sequence.go \
choice.go \ choice.go \
@ -36,7 +36,7 @@ head.go: .build/head.go
.build/headexported.go: $(sources) .build .build/headexported.go: $(sources) .build
go fmt ./... go fmt ./...
go run scripts/createhead.go --exported -- \ go run script/createhead.go --exported -- \
char.go \ char.go \
sequence.go \ sequence.go \
choice.go \ choice.go \
@ -75,12 +75,12 @@ lib: $(sources) head.go headexported.go internal/self/self.go
go build ./internal/self go build ./internal/self
cmd/treerack/docreflect.gen.go: $(sources) .build 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 go fmt .build/docreflect.gen.go
mv .build/docreflect.gen.go cmd/treerack mv .build/docreflect.gen.go cmd/treerack
cmd/treerack/readme.md: $(sources) cmd/treerack/docreflect.gen.go 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 rm cmd/treerack/readme.md
.build/treerack: $(sources) lib cmd/treerack/docreflect.gen.go .build .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 .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 rm .build/treerack.1
build: lib cmd/treerack/readme.md .build/treerack .build/treerack.1 build: lib cmd/treerack/readme.md .build/treerack .build/treerack.1
@ -135,8 +135,9 @@ cpu.out: $(sources)
cpu: cpu.out cpu: cpu.out
go tool pprof -top 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 fmt ./...
go run script/format.go $(parsers)
$(prefix)/bin/treerack: .build/treerack $(prefix)/bin/treerack: .build/treerack
mkdir -p $(prefix)/bin mkdir -p $(prefix)/bin

View File

@ -1 +1 @@
foo = "bar" foo = "bar";

View File

@ -5,7 +5,7 @@ ignore:ws = " " | [\t] | [\r] | [\n];
// between the digits and the delimiters. We support integers, floating point numbers, and floating point // 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 // 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. // 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: // define the supported operators:
add = "+"; add = "+";
@ -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 // 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. // the expression is the root parser of the syntax.
result = expression | "exit" result = expression | "exit";

View File

@ -4,7 +4,7 @@ true = "true";
false = "false"; false = "false";
null = "null"; null = "null";
string:nows = "\"" ([^\\"\b\f\n\r\t] | "\\" (["\\/bfnrt] | "u" [0-9a-f]{4}))* "\""; 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; entry = string ":" expression;
object = "{" (entry ("," entry)*)? "}"; object = "{" (entry ("," entry)*)? "}";
array = "[" (expression ("," expression)*)? "]"; array = "[" (expression ("," expression)*)? "]";

View File

@ -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")*;

View File

@ -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 | ";")*;

View File

@ -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?;

View File

@ -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?;

225
doc/example/mml.treerack Normal file
View File

@ -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?;

View File

@ -1,10 +1,9 @@
whitespace:ws = [ \b\f\n\r\t\v]; whitespace:ws = [ \b\f\n\r\t\v];
comment:ws = ";" [^\n]*; 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 = "\"" ([^\\"] | "\\" .)* "\""; string:nows = "\"" ([^\\"] | "\\" .)* "\"";
symbol:nows = ([^\\ \n\t\b\f\r\v\"()\[\]#] | "\\" .)+; symbol:nows = ([^\\ \n\t\b\f\r\v\"()\[\]#] | "\\" .)+;
list-form:alias = "(" expression* ")" list-form:alias = "(" expression* ")" | "[" expression* "]";
| "[" expression* "]";
list = list-form; list = list-form;
vector = "#" list-form; vector = "#" list-form;
expression:alias = number | string | symbol | list; expression:alias = number | string | symbol | list;

View File

@ -1,6 +1,6 @@
whitespace:ws = [ \b\f\n\r\t\v]; whitespace:ws = [ \b\f\n\r\t\v];
comment:ws = ";" [^\n]*; 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 = "\"" ([^\\"] | "\\" .)* "\""; string:nows = "\"" ([^\\"] | "\\" .)* "\"";
symbol:nows = ([^\\ \n\t\b\f\r\v\"()] | "\\" .)+; symbol:nows = ([^\\ \n\t\b\f\r\v\"()] | "\\" .)+;
list = "(" expression* ")"; list = "(" expression* ")";

View File

@ -531,7 +531,7 @@ $ go run .
> exit > 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 ## Important note: unescaping
@ -623,7 +623,7 @@ following workflow:
- the detailed documentation of the treerack definition language: [./syntax.md](./syntax.md) - 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, - treerack command help: [../cmd/treerack/readme.md](../cmd/treerack/readme.md) or, if the command is installed,
`man treerack`, or `path/to/treerack help` `man treerack`, or `path/to/treerack help`
- the arithmetic calculator example: [./examples/acalc](./examples/acalc). - the arithmetic calculator example: [./example/acalc](./example/acalc).
- additional examples: [./examples](./examples) - additional example: [./example](./example)
Happy parsing! Happy parsing!

View File

@ -114,6 +114,6 @@ Comments follow C-style syntax and are ignored by the definition parser.
## Examples ## Examples
- [JSON](examples/json.treerack) - [JSON](example/json.treerack)
- [Scheme](examples/scheme.treerack) - [Scheme](example/scheme.treerack)
- [Treerack (itself)](../syntax.treerack) - [Treerack (itself)](../syntax.treerack)

View File

@ -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")*;

View File

@ -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 | ";")*;

View File

@ -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?;

View File

@ -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?;

View File

@ -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?;

View File

@ -276,7 +276,7 @@ func TestErrorVerbose(t *testing.T) {
// "c":3, // "c":3,
// }` // }`
// //
// s, err := openSyntaxFile("examples/json.treerack") // s, err := openSyntaxFile("example/json.treerack")
// if err != nil { // if err != nil {
// t.Error(err) // t.Error(err)
// return // return

View File

@ -341,6 +341,7 @@ func formatSequenceItemNode(out io.Writer, targetWidth int, n *Node) error {
} }
} }
fprint("}")
return err 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 return nil
} }

View File

@ -396,7 +396,8 @@ flag:alias = alias | ws | nows | kw | nokw | failpass | root;
definition-name:alias:nows = symbol (":" flag)*; definition-name:alias:nows = symbol (":" flag)*;
definition = definition-name "=" expression; definition = definition-name "=" expression;
definitions:alias = definition (";"+ definition)*; definitions:alias = definition (";"+ definition)*;
syntax:root = ";"* definitions? ";"*;` syntax:root = ";"* definitions? ";"*;
`
func TestDocFormat(t *testing.T) { func TestDocFormat(t *testing.T) {
for _, test := range []struct{ title, in, out string }{{ for _, test := range []struct{ title, in, out string }{{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -285,7 +285,7 @@ func jsonTreeToJSON(n *Node) (interface{}, error) {
} }
func TestJSON(t *testing.T) { func TestJSON(t *testing.T) {
runTestsFile(t, "docs/examples/json.treerack", []testItem{{ runTestsFile(t, "doc/example/json.treerack", []testItem{{
title: "true", title: "true",
text: "true", text: "true",
node: &Node{ node: &Node{
@ -509,7 +509,7 @@ func TestRandomJSON(t *testing.T) {
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
s, err := openSyntaxFile("docs/examples/json.treerack") s, err := openSyntaxFile("doc/example/json.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -3,7 +3,7 @@ package treerack
import "testing" import "testing"
func TestKeyVal(t *testing.T) { func TestKeyVal(t *testing.T) {
runTestsFile(t, "docs/examples/keyval.treerack", []testItem{{ runTestsFile(t, "doc/example/keyval.treerack", []testItem{{
title: "empty", title: "empty",
}, { }, {
title: "a comment", title: "a comment",

View File

@ -3,7 +3,7 @@ package treerack
import "testing" import "testing"
func TestMML(t *testing.T) { func TestMML(t *testing.T) {
s, err := openSyntaxFile("docs/examples/mml.treerack") s, err := openSyntaxFile("doc/example/mml.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -5,7 +5,7 @@ import (
) )
func TestMMLExp2(t *testing.T) { func TestMMLExp2(t *testing.T) {
s, err := openSyntaxFile("docs/examples/mml-exp2.treerack") s, err := openSyntaxFile("doc/example/mml-exp2.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -5,7 +5,7 @@ import (
) )
func TestMMLExp3(t *testing.T) { func TestMMLExp3(t *testing.T) {
s, err := openSyntaxFile("docs/examples/mml-exp3.treerack") s, err := openSyntaxFile("doc/example/mml-exp3.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -9,7 +9,7 @@ import (
) )
func TestMMLExp(t *testing.T) { func TestMMLExp(t *testing.T) {
s, err := openSyntaxFile("docs/examples/mml-exp.treerack") s, err := openSyntaxFile("doc/example/mml-exp.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -2987,7 +2987,7 @@ func TestMMLFile(t *testing.T) {
const n = 180 const n = 180
s, err := openSyntaxFile("docs/examples/mml-exp.treerack") s, err := openSyntaxFile("doc/example/mml-exp.treerack")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -2995,7 +2995,7 @@ func TestMMLFile(t *testing.T) {
s.Init() s.Init()
f, err := os.Open("docs/examples/test.mml") f, err := os.Open("doc/example/test.mml")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -8,8 +8,8 @@ its Abstract Syntax Tree (AST) representation. It utilizes a custom syntax defin
## Examples ## Examples
- **JSON**: [docs/examples/json.treerack](docs/examples/json.treerack) - **JSON**: [doc/example/json.treerack](doc/example/json.treerack)
- **Scheme**: [docs/examples/scheme.treerack](docs/examples/scheme.treerack) - **Scheme**: [doc/example/scheme.treerack](doc/example/scheme.treerack)
- **Treerack (self-definition)**: [syntax.treerack](syntax.treerack) - **Treerack (self-definition)**: [syntax.treerack](syntax.treerack)
## Overview ## Overview
@ -42,8 +42,8 @@ go install code.squareroundforest.org/arpio/treerack/cmd/treerack
## Documentation ## Documentation
- [Manual](docs/manual.md): a guide to the main use cases supported by Treerack. - [Manual](doc/manual.md): a guide to the main use cases supported by Treerack.
- [Syntax Definition](docs/syntax.md): detailed reference for the Treerack definition language. - [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 - [Library Documentation](https://godocs.io/code.squareroundforest.org/arpio/treerack): GoDoc reference for the
runtime library. runtime library.

View File

@ -3,7 +3,7 @@ package treerack
import "testing" import "testing"
func TestScheme(t *testing.T) { func TestScheme(t *testing.T) {
runTestsFile(t, "docs/examples/scheme.treerack", []testItem{{ runTestsFile(t, "doc/example/scheme.treerack", []testItem{{
title: "empty", title: "empty",
}, { }, {
title: "a function", title: "a function",

View File

@ -141,7 +141,7 @@ func main() {
} }
fmt.Printf( 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, packageName,
varName, varName,
quotedCode, quotedCode,

46
script/format.go Normal file
View File

@ -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)
}
}
}

View File

@ -3,7 +3,7 @@ package treerack
import "testing" import "testing"
func TestSExpr(t *testing.T) { func TestSExpr(t *testing.T) {
runTestsFile(t, "docs/examples/sexpr.treerack", []testItem{{ runTestsFile(t, "doc/example/sexpr.treerack", []testItem{{
title: "number", title: "number",
text: "42", text: "42",
nodes: []*Node{{ nodes: []*Node{{

View File

@ -483,7 +483,7 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
fprintln() fprintln()
fprintln() fprintln()
// generate headCode with scripts/createhead.go // generate headCode with script/createhead.go
hc := headCode hc := headCode
if o.Export { if o.Export {
hc = headCodeExported hc = headCodeExported

View File

@ -2,44 +2,54 @@
foo foo
*/ */
// bar // bar
// bar // bar
//baz // baz
/* foo /* foo
bar baz */// foo bar baz bar baz */ // foo bar baz
wschar:alias =// foo wschar:alias = // foo
/* bar */ " " | "\t" | "\n" | "\b" | "\f" | "\r" | "\v"; /* bar */
" " | "\t" | "\n" | "\b" | "\f" | "\r" | "\v";
wsc:ws = wschar | comment; wsc:ws = wschar | comment;
block-comment:alias:nows = /* foo */ // bar
block-comment:alias:nows /* foo */ // bar "/*" ("*" [^/] | [^*])* "*/";
= "/*" ("*" [^/] | [^*])* "*/"; line-comment:alias:nows = /*
line-comment:alias:nows /* foo
foo */
*/ = "//" [^\n]*; "//" [^\n]*;
comment-segment:alias:nows = // bar comment-segment:alias:nows = // bar
line-comment | block-comment; line-comment | block-comment;
ws-no-nl:alias:nows = " " | "\t" | "\b" | /* this one */ /* is a */ "\f" /* form feed */ /* for sure */ | "\r" | "\v"; ws-no-nl:alias:nows = " "
comment:nows = comment-segment /* segment is not the best name */ /* but */ (ws-no-nl* "\n"? ws-no-nl* /* fine */ comment-segment)*; | "\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 [^] any-char = "."; // equivalent to [^]
// caution: newline is accepted // caution: newline is accepted
/* class not */ class-not = "^"; /* class not */
class-char:nows = [^\\\[\]\^\-] | "\\" . /* foo class-not = "^";
bar */; class-char:nows = [^\\\[\]\^\-] | "\\" .; /* foo
char-range:nows = class-char "-" class-char // foo bar */
; char-range:nows = class-char "-" class-char; // foo
char-class:nows = "[" class-not? (class-char | char-range)* "]"; char-class:nows = "[" class-not? (class-char | char-range)* "]";
// newline is accepted // newline is accepted
sequence-char:nows = [^\\"] | "\\" .; sequence-char:nows = [^\\"] | "\\" .;
char-sequence:nows = "\"" sequence-char* "\""; char-sequence:nows = "\"" sequence-char* "\"";
terminal:alias = any-char | char-class | char-sequence; terminal:alias = any-char | char-class | char-sequence;
symbol:nows = [^\\ \n\t\b\f\r\v/.\[\]\"{}\^+*?|():=;]+; symbol:nows = [^\\ \n\t\b\f\r\v/.\[\]\"{}\^+*?|():=;]+;
group:alias = "(" expression ")"; group:alias = "(" expression ")";
number:alias:nows = [0-9]+; number:alias:nows = [0-9]+;
count = number; count = number;
count-quantifier = "{" count "}"; count-quantifier = "{" count "}";
@ -49,15 +59,9 @@ range-quantifier = "{" range-from? "," range-to? "}";
one-or-more = "+"; one-or-more = "+";
zero-or-more = "*"; zero-or-more = "*";
zero-or-one = "?"; zero-or-one = "?";
quantity:alias = count-quantifier quantity:alias = count-quantifier | range-quantifier | one-or-more | zero-or-more | zero-or-one;
| range-quantifier
| one-or-more
| zero-or-more
| zero-or-one;
item:nows = (terminal | symbol | group) quantity?; item:nows = (terminal | symbol | group) quantity?;
sequence = item+; sequence = item+;
option:alias = terminal | symbol | group | sequence; option:alias = terminal | symbol | group | sequence;
// DOC: how the order matters // DOC: how the order matters
@ -66,7 +70,6 @@ choice = option ("|" option)+;
// DOC: not having 'not' needs some tricks sometimes // DOC: not having 'not' needs some tricks sometimes
expression:alias = terminal | symbol | group | sequence | choice; expression:alias = terminal | symbol | group | sequence | choice;
alias = "alias"; alias = "alias";
ws = "ws"; ws = "ws";
nows = "nows"; nows = "nows";
@ -77,6 +80,5 @@ 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-name:alias:nows = symbol (":" flag)*;
definition = definition-name "=" expression; definition = definition-name "=" expression;
definitions:alias = definition (";"+ definition)*; definitions:alias = definition (";"+ definition)*;
syntax:root = ";"* definitions? ";"*; syntax:root = ";"* definitions? ";"*;