Repository navigation
Fenia Grammar
TL;DR: The formal grammar of Fenia as the parser accepts it today, with tokens and operator precedence. It is regenerated from the yacc source, not written by hand. Use when: you need to know exactly what parses, for example when writing a linter, an editor mode or a code generator. Source of truth:
dreamland_code/src/fenia/parse.y++(rules and precedence),scan.l++(tokens) Verified: 2026-11-02 against dreamland_code 88da8302
For the meaning of each construct, types and runtime rules see Fenia-Language. This page only says what is accepted.
The language started as a mob-program language for another MUD in 2004. The parser headers carry that year and two authors. The history is on History.
| Token | Matches |
|---|---|
IDENT |
[A-Za-z_] or a Cyrillic letter, then letters, digits, _
|
NUMBER |
[0-9]+ (read with atoi), and the words true (1) and false (0) |
STRING |
'...' or "...", escapes \n \r \t, any other \x gives x, may span lines |
| keywords |
null if in else for break continue return function var try catch throw and this
|
| punctuation |
< > - + = * / ( ) ! ~ % | ^ , . { } [ ] ; and &
|
| two-character |
== != ->
|
| comments |
// ... to end of line, /* ... */ nested |
There is no >= or <= token. The grammar builds them from > followed by =. && and || are likewise two & or two | tokens in a row.
Declared in parse.y++, lowest first. right means right-associative.
| Level | Operators |
|---|---|
| 1 |
-> (right) |
| 2 |
= (right) |
| 3 | logical && || (left, rule level) |
| 4 | | |
| 5 | ^ |
| 6 | & |
| 7 |
== !=
|
| 8 |
< > (<=, >= take the same level) |
| 9 |
+ -
|
| 10 |
* / %
|
| 11 | unary ! ~ - (right) |
| 12 |
. ( [ (left) |
The logical rules carry a lower precedence than the lookahead tokens | and &. When the parser sees a && b || c it prefers to shift, so the chain groups to the right: a && (b || c). This was derived by replaying this table in an LALR parser generator, not by running the engine (unverified on the engine). Parenthesize mixed && and ||.
Generated from the rules in parse.y++ with the semantic actions removed. (empty) is the empty production. Single quotes mark literal tokens.
input
: exp
stmts
: (empty)
| stmts stmt
| stmts error
stmt
: stmt_aux
stmt_aux
: ';'
| '{' stmts '}'
| 'if' '(' exp ')' stmt
| 'if' '(' exp ')' stmt 'else' stmt
| 'for' '(' explist ';' exp ';' explist ')' stmt
| 'for' '(' explist ';' ';' explist ')' stmt
| 'for' '(' id 'in' exp ')' stmt
| 'break' ';'
| 'continue' ';'
| exp ';'
| 'return' ';'
| 'return' exp ';'
| 'throw' exp ';'
| 'var' arguments ';'
| 'try' stmt 'catch' '(' id ')' stmt
nonnull_explist
: exp
| nonnull_explist ',' exp
explist
: (empty)
| nonnull_explist
exp
: binary_exp
| exp '&' '&' exp
| exp '|' '|' exp
| reference '=' exp
| lambda
lambda_rh
: '->' exp
| '->' '{' stmts '}'
lambda
: id lambda_rh
| '(' ')' lambda_rh
| '(' nonnull_arguments ',' id ')' lambda_rh
binary_exp
: unary_exp
| exp '+' exp
| exp '*' exp
| exp '-' exp
| exp '/' exp
| exp '&' exp
| exp '|' exp
| exp '%' exp
| exp '^' exp
| exp '==' exp
| exp '!=' exp
| exp '>' exp
| exp '>' '=' exp
| exp '<' exp
| exp '<' '=' exp
unary_exp
: primary
| '!' unary_exp
| '~' unary_exp
| '-' unary_exp
id
: IDENT
reference
: id
| '.' id
| primary '.' id
| primary '[' exp ']'
primary
: reference
| reference '(' explist ')'
| '[' nonnull_explist ']'
| '(' exp ')'
| constant
| 'function' '(' arguments ')' '{' stmts '}'
| 'function' NUMBER '(' arguments ')' '{' stmts '}'
| 'function' NUMBER
constant
: 'null'
| NUMBER
| string
arguments
: (empty)
| nonnull_arguments
nonnull_arguments
: id
| nonnull_arguments ',' id
string
: STRING
| string STRING
-
One expression per file.
inputis a singleexp. A statement list only appears insidefunction ( ) { ... }and-> { ... }. -
vartakes names only.'var' arguments ';'is a list of identifiers. There is no initializer. -
A call needs a reference on the left. The rule is
reference '(' explist ')'. A call on a call result or on a parenthesized function does not parse. -
Assignment targets are a reference: a name,
.name,x.nameorx[expr]. Assignment is an expression, so it is allowed insideif (...)conditions. -
[a, b, c]is not a list literal. The rule'[' nonnull_explist ']'evaluates every element and yields the last one. It cannot be empty. -
forhas two C-style forms (with and without a condition) and thefor (id in exp)form.varis not allowed in the init part. -
stmts erroris the recovery rule. A bad statement inside a body is reported asstatement expectedand parsing continues, but the file is still rejected. -
Numbered functions (
function 12 (a) { }andfunction 12) are how the engine writes stored closures back out. Do not write them by hand. -
Lambdas need a single bare name,
(), or two or more names in parentheses.(a) -> awith one parenthesized name does not match any lambda rule. - Missing on purpose:
while,do,switch,?:,++,--, compound assignment,new, object and array literals,else ifas a keyword.
Start here
Get running
Contribute
- Contribute
- Contributing Workflow
- Code Style
- Accessibility
- Writing Player Text
- Game Design Principles
Build areas
- Building
- OLC
- OLC Rooms
- OLC Mobiles
- OLC Objects
- OLC Resets
- OLC Line Editor
- OLC Other Editors
- Area Format
- Area Quests
- Mob Forms and Parts
Fenia
- Fenia
- Fenia Language
- Fenia Triggers
- Fenia Gotchas
- Fenia API
- Fenia Scripts
- Fenia Behaviors
- Fenia Skills and Spells
- Fenia Cookbook
- Fenia Grammar
Engine
- Engine Architecture
- Engine Time and Pulses
- Output Format
- Mechanics Combat
- Mechanics World
- Data Configs
- Item Power
Localization
Clients
More