language2.bnf (4217B)
# Mighty language2 sketch grammar
program ::= { nl | top nl }
nl ::= NEWLINE { NEWLINE }
top ::= namespace | use | decl
namespace ::= "ns" path
use ::= "use" path
decl ::= [ "pub" ] ( struct | function | operator )
struct ::= "struct" Type [ generics ] "::" block(member)
member ::= field | decl
field ::= type names
names ::= name { "," name }
function ::= { pre nl } signature { where_part } body
pre ::= "pre" expr
where_part ::= [ nl ] where
where ::= "where" constraint # may mention contextual `result`
signature ::= type name [ generics ] "(" [ params ] ")"
params ::= param { "," param }
param ::= type [ name ]
body ::= [ nl ] "=" ( expr | nl expr ) | "::" block(stmt)
operator ::= "op" op [ generics ] "(" params ")" "->" type body
stmt ::= binding | ret | expr
binding ::= type name "=" expr
ret ::= "ret" expr
generics ::= "[" typevar { "," typevar } "]"
typevar ::= Type
constraint ::= expr | type_constraint
type_constraint ::= Type ":" trait { "+" trait }
trait ::= Type
type ::= primary_type { type_suffix }
primary_type ::= path [ type_args ] | "(" types ")"
type_args ::= "[" type { "," type } "]"
types ::= type { "," type }
type_suffix ::= "[]" # slice/dynamic array
| "[" int_lit "]" # fixed array
| "?" # option: T?
| "!" type # result: T!E
path ::= name { "." name }
expr ::= conditional
conditional ::= pipe [ "if" expr "else" expr ]
pipe ::= scan { "|" ( map | expr ) }
map ::= "@" "(" expr ")" # each: `.` is the current element
scan ::= [ op "\\" ] reduce # +\xs, *\xs
reduce ::= [ op "/" ] logic # +/xs, */xs, max/xs
logic ::= compare { ( "and" | "or" ) compare }
compare ::= range { cmp range }
range ::= add [ ".." [ add ] ]
add ::= mul { ( "+" | "-" ) mul }
mul ::= pow { ( "*" | "/" ) pow }
pow ::= unary { "^" unary }
unary ::= [ "-" | "#" ] postfix # #xs is length/count
postfix ::= atom { call | selector | "." name }
call ::= "(" [ args ] ")"
args ::= expr { "," expr }
selector ::= "[" [ items ] "]" # index, gather, mask, or filter expr
atom ::= literal | path | subject | array | record | "(" expr ")"
subject ::= "." [ name ] # current subject/self/element
array ::= "[" [ items ] "]"
items ::= expr { "," expr }
record ::= path "(" [ fields ] ")"
fields ::= field_value { "," field_value }
field_value ::= name ":" expr
literal ::= int_lit | float_lit | string | "true" | "false" | "nil"
op ::= "+" | "*" | "&" | "|" | "min" | "max" | name
cmp ::= "==" | "!=" | "<" | "<=" | ">" | ">="
block(x) ::= INDENT { nl | x nl } DEDENT
# Lexical sketch: name is an identifier, Type is a capitalized identifier.
# Comments run from // to end of line.
# Selector semantics:
# xs[i] scalar index
# xs[[0, 2]] gather by int[] indices
# xs[mask] compress by bool[] mask
# xs[.valid] filter by expression evaluated per element
# Array literals use commas: [1, 2, 3], [true, false, true].
# Named record literals use Type(field: value): Vec2(x: 1.0, y: 2.0).
# Ranges are half-open. Negative indices are relative to the end.
# xs[0..-1] selects from the first element up to, not including, the last.
# Reductions over empty collections are invalid unless the operator has a
# defined identity. max/ and min/ require non-empty collections.
# Values are passed by copy semantically. An implementation may pass by
# read-only reference when that is observationally identical.
# `.` semantics:
# In methods, `.` is the receiver. In free functions, `.` is the first argument.
# In selectors and @(...), `.` is the current element.