Grammar
Note for new readers: This chapter is included mainly to give a complete structural view of the language. You do not need to read the full grammar to start using or understanding NEX. If the notation feels unfamiliar, it is perfectly fine to skim this page and return to it later as a reference.
This chapter gives a compact view of the current NEX surface grammar. It is meant as a structural summary for readers who want to see how the language fits together after reading the surrounding reference chapters.
Like most practical parsers, NEX also has a few rules that are checked outside
the grammar itself. For example, return is only valid inside a function body,
and function declarations are accepted only at the top level of a program even
though the compact statement grammar shows them beside other statement forms.
Surface grammar
<program> ::= <statement>* EOF
<statement> ::= <typed-decl>
| <function-decl>
| <return-stmt>
| <if-stmt>
| <while-stmt>
| <for-stmt>
| <block>
| <assignment-stmt>
| <expr-stmt>
<typed-decl> ::= <typed-decl-core> ";"
<typed-decl-core> ::= <scalar-typed-decl-core>
| <array-decl-core>
<scalar-typed-decl-core> ::= <scalar-type> <identifier> "=" <expression>
<array-decl-core> ::= <array-type> <identifier>
<type> ::= <scalar-type> | <array-type>
<scalar-type> ::= "int" | "str" | "bool"
<array-type> ::= "array" "<" ( "int" | "str" ) ">"
<function-decl> ::= "fn" <identifier> "(" [ <parameters> ] ")" "->" <return-type> <block>
<parameters> ::= <parameter> ("," <parameter>)*
<parameter> ::= <type> <identifier>
<return-type> ::= <type> | "void"
<return-stmt> ::= "return" [ <expression> ] ";"
<if-stmt> ::= "if" "(" <expression> ")" <block> [ "else" <block> ]
<while-stmt> ::= "while" "(" <expression> ")" <block>
<for-stmt> ::= "for" "(" <for-init> ";" <expression> ";" <for-iter> ")" <block>
<for-init> ::= empty
| <typed-decl-core>
| <assignment-core>
| <expression>
<for-iter> ::= empty
| <assignment-core>
| <expression>
<block> ::= "{" <statement>* "}"
<assignment-stmt> ::= <assignment-core> ";"
<assignment-core> ::= <assignment-target> <assignment-op> <expression>
<assignment-op> ::= "=" | "+=" | "-=" | "*=" | "/=" | "^="
<assignment-target> ::= <identifier> | <index-expr>
<expr-stmt> ::= <expression> ";"
<expression> ::= <logical-or>
<logical-or> ::= <logical-and> ( "||" <logical-and> )*
<logical-and> ::= <comparison> ( "&&" <comparison> )*
<comparison> ::= <term> (( "<" | ">" | "<=" | ">=" | "==" | "!=" ) <term>)*
<term> ::= <factor> (("+" | "-") <factor>)*
<factor> ::= <power> (("*" | "/" | "%") <power>)*
<power> ::= <unary> [ "^" <power> ]
<unary> ::= ("-" | "!") <unary>
| <postfix>
<postfix> ::= <primary> ( <call-suffix> | <index-suffix> | <method-suffix> | <postfix-update> )*
<call-suffix> ::= "(" [ <arguments> ] ")"
<index-suffix> ::= "[" <expression> "]"
<method-suffix> ::= "." <identifier> "(" [ <arguments> ] ")"
<postfix-update> ::= "++" | "--"
<index-expr> ::= <postfix> <index-suffix>
<primary> ::= <number>
| <string>
| "true"
| "false"
| <identifier>
| "(" <expression> ")"
<arguments> ::= <expression> ("," <expression>)*
Syntax diagrams
<program>
<statement>
<typed-decl>
<typed-decl-core>
<scalar-typed-decl-core>
<array-decl-core>
<type>
<scalar-type>
<array-type>
<function-decl>
<parameters>
<parameter>
<return-type>
<return-stmt>
<if-stmt>
<while-stmt>
<for-stmt>
<for-init>
<for-iter>
<block>
<assignment-stmt>
<assignment-core>
<assignment-op>
<assignment-target>
<expr-stmt>
<expression>
<logical-or>
<logical-and>
<comparison>
<term>
<factor>
<power>
<unary>
<postfix>
<call-suffix>
<index-suffix>
<method-suffix>
<postfix-update>
<index-expr>
<primary>
<arguments>
Notes
- Function calls are postfix expressions, not statements in their own right.
That is why built-in functions such as
print(...)andinput()can appear in an initializer, inside another call, or as a plain expression statement. - Function declarations are top-level declarations. They are listed under
<statement>so the grammar can show their source shape, but declarations inside blocks or other functions are rejected by the parser. - Array declarations are syntactically distinct from scalar declarations:
array<int> arr;is valid, while array declarations with initializers are currently rejected. - Postfix
++and--are also part of the expression grammar. In the current implementation they are restricted to variable operands. - Postfix expressions now also include array indexing such as
arr[-1]and method-style calls such asarr.length(),arr.resize(3), orarr.reset(). forreuses declaration, assignment, and expression forms in its header, but without extra trailing semicolons inside those clauses.- The grammar allows repeated comparison operators syntactically. Runtime type rules still determine whether a particular chained comparison is meaningful.
- Logical operators are part of the expression grammar, with
&&binding more tightly than||.
For the meaning of each construct, the surrounding reference chapters remain the authoritative explanation. This chapter is mainly a compact syntax map.