(* =========================================================================== ELPL LANGUAGE — FULL GRAMMAR =========================================================================== Sources: Lexer.java, Parser.java, LLVMCompiler.java, JVMCompiler.java Two backends (LLVM native, JVM via ASM) share this single surface syntax. =========================================================================== *) (* --------------------------------------------------------------------------- PROGRAM STRUCTURE --------------------------------------------------------------------------- *) program = [ package_decl ] { sep } { top_item { sep } } EOF ; package_decl = "package" qualified_name [ ";" ] ; top_item = import_decl | class_decl | interface_decl | enum_decl | struct_decl | extern_decl | function_decl | statement ; import_decl = "use" ( string_lit | library_path ) ; library_path = identifier { "." ( identifier | "*" ) } ; (* --------------------------------------------------------------------------- MODIFIERS --------------------------------------------------------------------------- *) modifiers = { modifier } ; modifier = "public" | "private" | "protected" | "static" | "abstract" | "final" | "@override" ; (* --------------------------------------------------------------------------- TYPE DECLARATIONS --------------------------------------------------------------------------- *) class_decl = modifiers "class" identifier [ "extends" qualified_name ] [ "implements" type { "," type } ] "{" { sep } { class_member { sep } } "}" ; class_member = modifiers ( class_decl (* nested class *) | interface_decl (* nested interface *) | enum_decl (* nested enum *) | struct_decl (* nested struct *) | function_decl | field_decl ) ; field_decl = variable_decl ; interface_decl = modifiers "interface" identifier [ "extends" identifier ] "{" { sep } { function_decl { sep } } "}" ; enum_decl = modifiers "enum" identifier [ "(" identifier { "," identifier } ")" ] "{" { sep } { enum_constant [ "," ] { sep } } "}" ; enum_constant = identifier [ "(" [ expr_list ] ")" ] ; struct_decl = modifiers "struct" identifier "{" { sep } { modifiers variable_decl { sep } } "}" ; (* --------------------------------------------------------------------------- EXTERN (FFI — required for the LLVM backend) --------------------------------------------------------------------------- *) extern_decl = "extern" ( extern_function | extern_variable ) ; extern_function = "function" identifier "(" [ param_list ] ")" [ ":" type ] ; extern_variable = "let" identifier ":" type ; (* --------------------------------------------------------------------------- FUNCTIONS --------------------------------------------------------------------------- *) function_decl = modifiers "function" identifier "(" [ param_list ] ")" [ ":" type ] ( block | ε ) ; (* body omitted iff abstract / extern *) param_list = param { "," param } ; param = [ type ] identifier ; (* absent type ⇒ dynamic/any *) (* --------------------------------------------------------------------------- STATEMENTS --------------------------------------------------------------------------- *) statement = use_stmt | print_stmt | printnl_stmt | printerr_stmt | printerrnl_stmt | nl_stmt | if_stmt | for_stmt | repeat_stmt | while_stmt | switch_stmt | foreach_stmt | call_stmt | super_call | stop_stmt | continue_stmt | return_stmt | throw_stmt | try_stmt | thread_stmt | sleep_stmt | join_stmt | collection_decl | java_block | array_assign | map_assign | variable_decl | expression_stmt ; (* ------- trivial keyword statements ------- *) nl_stmt = "nl" ; stop_stmt = "stop" ; (* break — requires enclosing loop/switch *) continue_stmt = "continue" ; sleep_stmt = "sleep" "=>" expr ; join_stmt = "join" identifier ; java_block = "@java" "{" "}" ; (* ------- print family (same-line greedy) ------- *) print_stmt = "print" concat_expr { concat_expr } ; printnl_stmt = "printnl" concat_expr { concat_expr } ; printerr_stmt = "printerr" concat_expr { concat_expr } ; printerrnl_stmt = "printerrnl" concat_expr { concat_expr } ; (* ------- control flow ------- *) if_stmt = "if" expr "then" block [ "otherwise" ( if_stmt | block ) ] ; for_stmt = "for" identifier "be" expr "to" expr block ; repeat_stmt = "repeat" number_lit "times" block ; while_stmt = "while" expr block ; foreach_stmt = "foreach" identifier [ "," identifier ] ":" expr block ; switch_stmt = "switch" "(" expr ")" "{" { switch_case } [ switch_default ] "}" ; switch_case = "case" expr "=>" { statement } ; switch_default = "default" "=>" { statement } ; (* ------- call / return / exception ------- *) call_stmt = "call" identifier [ "(" [ expr_list ] ")" ] ; super_call = "super" "(" [ expr_list ] ")" ; return_stmt = "return" [ concat_expr ] ; throw_stmt = "throw" expr ; try_stmt = "try" block "catch" identifier block ; (* ------- concurrency ------- *) thread_stmt = "Thread" identifier "{" { sep } { statement { sep } } "}" ; (* ------- collection declarations ------- *) collection_decl = array_decl | map_decl | set_decl | queue_decl | list_decl ; array_decl = "Array" [ "<" type ">" ] identifier "be" ( "new" "Array" "<" type ">" "[" expr "]" | "[" [ expr_list ] "]" ) ; map_decl = "Map" ( "<" type "," type ">" | "[" identifier "," identifier "]" ) identifier "be" "{" [ map_entry { "," map_entry } ] "}" ; map_entry = expr ":" expr ; set_decl = "Set" [ "<" type ">" ] identifier "be" "{" [ expr_list ] "}" ; queue_decl = "Queue" [ "<" type ">" ] identifier "be" "{" [ expr_list ] "}" ; list_decl = "List" [ "<" type ">" ] identifier "be" "[" [ expr_list ] "]" ; (* ------- assignment / declaration ------- *) array_assign = identifier { "[" expr "]" } assign_op expr ; map_assign = identifier "[" expr "]" "be" expr ; variable_decl = [ "final" ] ( "float" identifier assign_tail | "let" identifier [ ":" type ] assign_tail | type identifier assign_tail | member_ref assign_op expr ) ; assign_tail = [ assign_op expr ] ; assign_op = "be" | "pels" | "mels" | "dels" | "sels" | "mls" ; member_ref = ( "this" | identifier ) "." identifier ; expression_stmt = expr ; (* --------------------------------------------------------------------------- BLOCK --------------------------------------------------------------------------- *) block = "{" { sep } { statement { sep } } "}" ; (* --------------------------------------------------------------------------- EXPRESSIONS --------------------------------------------------------------------------- *) expr = ternary_expr ; ternary_expr = or_expr [ "?" expr ":" expr ] ; or_expr = and_expr { "or" and_expr } ; and_expr = equality_expr { "and" equality_expr } ; equality_expr = relational_expr { ( "is equal to" | "not equal to" ) relational_expr } ; relational_expr = additive_expr { ( "is greater than" | "is less than" | "is greater than or equal to" | "is less than or equal to" ) additive_expr } ; additive_expr = multiplicative_expr { ( "add" | "subtract" | "+" | "-" ) multiplicative_expr } ; multiplicative_expr = unary_expr { ( "multiply" | "divide" | "mod" | "*" | "/" | "%" ) unary_expr } ; (* same-line implicit concatenation (Perl/Raku-style) *) concat_expr = expr { expr } ; unary_expr = "not" unary_expr | "-" unary_expr | postfix_expr ; postfix_expr = primary { postfix_op } ; postfix_op = "." ( identifier | "add" ) [ "(" [ expr_list ] ")" ] | "[" expr "]" ; primary = "true" | "false" | "null" | number_lit | string_lit | "scan" "(" [ string_lit ] ")" | "new" new_expr | "call" identifier [ "(" [ expr_list ] ")" ] | array_literal | "this" member_chain | "super" member_chain | identifier ( class_literal | array_access | member_chain | function_call | ε ) | cast_expr | lambda_expr | "(" expr ")" ; new_expr = "Array" "<" type ">" "[" expr "]" (* native array alloc *) | qualified_name "(" [ expr_list ] ")" ; (* object ctor *) member_chain = { "." ( identifier | "add" ) [ "(" [ expr_list ] ")" ] } ; array_access = { "[" expr "]" } ; function_call = "(" [ expr_list ] ")" ; class_literal = "." "class" ; array_literal = "[" [ expr { "," expr } ] "]" ; cast_expr = "(" type ")" unary_expr ; (* lambdas are parsed by the bootstrapped ELPL meta-parser *) lambda_expr = "(" [ param_list ] ")" "=>" ( expr | block ) ; expr_list = expr { "," expr } ; (* --------------------------------------------------------------------------- TYPES --------------------------------------------------------------------------- *) type = primitive_type | collection_type | qualified_name [ type_args ] ; primitive_type = "Int" | "Double" | "Float" | "Boolean" | "String" | "Long" | "Char" | "Short" | "Byte" | "Object" | "Void" ; collection_type = ( "List" | "Array" | "Map" | "Set" | "Queue" ) [ type_args ] ; qualified_name = identifier { "." identifier } ; type_args = "<" type { "," type } ">" ; (* --------------------------------------------------------------------------- BUILT-IN FUNCTIONS (callable as expression primaries) --------------------------------------------------------------------------- *) built_in_fn = "length" | "sum" | "max" | "min" | "sqrt" | "abs" | "pow" | "floor" | "ceil" | "reverse" | "deepReverse" | "deepString" | "writeByte" | "readByte" | "writeShort" | "readShort" | "writeInt" | "readInt" | "writeLong" | "readLong" | "writeFloat" | "readFloat" | "writeDouble"| "readDouble" | "writePointer"| "readPointer" | "offsetPointer" ; (* --------------------------------------------------------------------------- STANDARD-LIBRARY MEMBER DISPATCH (Syntactically just `expr . identifier ( args )`; these are the semantic method names the two backends recognise.) --------------------------------------------------------------------------- *) (* io.* — file I/O *) io_members = "read" | "write" | "append" | "delete" | "exists" | "readLines" ; (* List / Array *) list_members = "append" | "add" | "insert" | "remove" ; (* Map *) map_members = "put" | "get" | "delete" | "has" | "clear" | "size" ; (* Set *) set_members = "include" | "remove" | "contains" | "size" | "isEmpty" | "clear" ; (* Queue *) queue_members = "affix" | "offer" | "poll" | "peek" | "remove" | "contains" | "size" | "isEmpty" | "clear" ; (* Enum instance *) enum_members = "name" | "ordinal" ; (* Math / LocalDate / LocalTime / LocalDateTime *) math_members = "sin" | "cos" | "tan" | "exp" | "log" | "sinh" | "asin" | "atan" | "atan2" | "pow" ; date_members = "now" | "of" ; (* --------------------------------------------------------------------------- LEXICAL STRUCTURE --------------------------------------------------------------------------- *) identifier = IDENT ; number_lit = DIGIT { DIGIT } [ "." DIGIT { DIGIT } ] [ ( "f" | "F" ) ] ; string_lit = '"' { ANYCHAR } '"' ; comment = line_comment | block_comment ; line_comment = "//" { ANYCHAR } NEWLINE ; block_comment = "" ; sep = NEWLINE | ";" ; (* =========================================================================== ADDITIONAL NOTES =========================================================================== 1. Keyword operators are word-based: add / subtract / multiply / divide / mod is equal to / not equal to is greater than / is less than is greater than or equal to / is less than or equal to and / or / not Symbolic operators are ALSO accepted in expressions ( - * / % + ). Comparison symbols (<, >) are NOT. 2. Two comments forms: `//` line comments and `<…!>` block comments. 3. `add` is dual-purpose: as an operator AND as a member name after '.' (e.g. `list.add(5)`), the parser special-cases it. 4. Implicit same-line concatenation: adjacent expressions on the same physical line form a ConcatNode (no explicit operator). 5. `print(...)` with a leading '(' is explicitly rejected — the print family consume space-separated expressions only. 6. Uninitialized declarations are permitted: `let x` / `float x` / `MyType x` terminate the statement at newline / keyword boundaries. 7. `final` is both a modifier (class members) and a local declaration prefix (`final let x be 5`). 8. Both backends recognise `.class` class-literals. 9. The two backends recognise different built-ins: JVM : length sum max min sqrt abs pow floor ceil reverse deepReverse deepString + io.read/write/append/delete/ exists/readLines + Math.* + LocalDate/LocalTime/ LocalDateTime + BuildConfig.* + R.* + enum.name()/ ordinal() LLVM : length + writeByte/readByte ... writePointer/readPointer/ offsetPointer (raw memory manager primitives) 10. LLVM backend rejects: @java{...} blocks, method dispatch on unknown classes, interface implementations (no vtable), and struct fields that are not simple declarations. 11. Lambda syntax is delegated to the ELPL-in-ELPL meta-parser (`ELPLParser`), so the surface form is: (a, b) => expr or (a, b) => { ... } but disambiguation with a cast `(Type) expr` is done at parse time via `isTypeCastLookahead` vs `isLambdaLookahead`. 12. Switch is non-fall-through; default must appear last because case bodies greedily consume statements until the next `case`, `default`, or `}`. 13. Enum constants may carry positional constructor arguments: enum Color(r, g, b) { RED(255,0,0), GREEN(0,255,0) } Field names declared after the enum name, arguments passed positionally per constant. 14. `Thread name { ... }` compiles to a pthread (LLVM) or a Runnable + java.lang.Thread (JVM). `sleep => n` sleeps n milliseconds. `join name` joins the named thread. 15. `foreach k : collection` iterates a single variable; `foreach k, v : map` iterates key/value pairs. The `:` separates the bindings from the collection expression. 16. `super(...)` is a statement-level parent-constructor call; `this(...)` is NOT supported. 17. `new` has two forms — dynamic array (`new Array[n]`) and object construction (`new Qualified.Name(args)`). `new List<…>` is not a valid form (Lists must be declared inline). 18. `be` is the universal "assign/initialize" verb; `pels`, `mels`, `dels`, `sels`, `mls` are compound-assignment short-forms whose semantics map to +=, -=, *=, /=, %= respectively (as confirmed by LLVMCompiler's switch and JVMCompiler's IADD/ISUB/IMUL/IDIV/IREM emission). 19. `float` is privileged as a declaration prefix (`float x be 5`) but `int`, `double`, `boolean`, `string` are NOT — they must be written as `let x: int be 5` etc. (`isPrimitiveOrBuiltInType` in Parser rejects the type-first form for these). 20. `void` is written `Void` (capitalized), and the compiler normalises `void` → Void with a friendly error otherwise. `List`, `Map`, `Set`, `Queue`, `Array` are reserved type words. 21. Imports may be: use "file.elpl" use elpl.sys.io use elpl.sys.time.* use android.widget.TextView The `use` keyword maps to C header import on the LLVM side (via `CHeaderImporter.resolveNativeAlias`) or to `import` declarations on the JVM side. 22. `R..` (Android resources) is specially intercepted at compile time and expanded to `/R$.` (GETSTATIC int). Same for `BuildConfig.` which is emitted as a GETSTATIC String, boolean, or int depending on field name. 23. `nl` keyword emits a NewlineNode that prints "\n"; distinct from an actual `\n` in source (which just becomes a separator). 24. `stop` and `continue` map to break/continue inside loops AND `stop` also works inside `switch` (it branches to the switch merge block). 25. Casts `(Type) expr` route through `isTypeCastLookahead`, which requires the parenthesised sequence to be a known type name AND be followed by the start of an expression. Otherwise the parentheses are treated as grouping. 26. The `end of file` token is always emitted by the lexer so synchronisation and error recovery can rely on it. =========================================================================== *)