This document describes the core language specifications and not particularly the current state of LIA-lang. This is simply the target of what LIA-lang should be.
This specification defines the normative syntax, semantics, and external interface of the LIA programming language. Its goal is to provide a precise, implementation‑agnostic contract for:
free)Scope: Only the core language and its required runtime behaviors.
Non‑scope: Tooling, build systems, optimization strategies, debugging facilities, editor integration, and prospective future extensions.
Conformance: An implementation is conforming if it:
null and void semantics).Undefined Behavior: Any construct not explicitly covered (e.g. accessing non‑existent object fields) is undefined and may terminate execution or produce implementation‑specific results.
Versioning: This draft targets v1.0.0. Backward incompatible changes after ratification require a major version increment.
All examples are illustrative; if an example conflicts with a formal rule, the rule governs.
Character set: ASCII (string contents treated as raw bytes)
Whitespace: space, tab, CR, LF (insignificant outside string literals)
Comments:
// to end of line/* to next */ comment closurePattern: [A-Za-z_][A-Za-z0-9_]*
Case-sensitive. Reserved keywords excluded.
function, class, new, var, const, return, if, else, for, loop, while, break, skip, free, import, true, false, null, void, bool, int, float, string
Note that keywords and their aliases aren't particularly reserved when defining variables
function: fn, fun, func, defvar: let, localconst: finalbreak: stopskip: continue, nextfree: delete, popnull: nulbool: boolean123).1.0, 0.5)." until next " (new lines do not close strings).true / false.null.{ <type>? <identifier>: <value> (, <type>? <identifier>: <value>)*, }.<type>? [element (, element)*] (optional leading inline type token).+ - * /===+ -ident[expression]() [] { } , :
Primitive core:
int<S> / uint<S> (signed / unsigned integers by size)float<S>stringboolvoid (function return type only)nullany (internal fallback / inference default)S = storage size in bytes; default size when omitted is 32 bits.
Default integer width: 32 bits (alias int = int32).
Integers (signed / unsigned):
int8, i8, byte / uint8, u8, ubyteint16, i16, short / uint16, u16, ushortint32, i32 / uint32, u32 (alias int = int32)int64, i64 / uint64, u64Floats:
float, float32, f32, number, numfloat64, f64, double, realStrings: string, str, text
Composite:
[...].{ type name: value, ... }.Syntax:
var <type>? <name> (= expression)?var <type>? <name>[index]? (array element assignment after declaration uses assignment)const <type>? <name> (= expression)? (var can be inferred)global var <type>? <name> (= expression)?const modifier is parsed; enforcement of immutability is nominal.
Variables with no declared type default to any
Uninitialized variables get null.
if (expr) { block } (else { block })?while (expr) { block }loop (num) { block }for (init; condition; increment) { block }return expressionfree(identifier (, identifier)*)import "path.lia" | "lib.llb"break (breaks out of loops)skip (skips current loop)Syntax:
function <return_type> <identifier>( <param_list>? ) { block }
Parameters:
<type> <identifier> separated by commas.
Return:
return <expression> (void functions may return without an expression producing null).
No overloading or default parameters.
Literal:
{ <type> <fieldName>: <expression>, ... }
Element type is either explicitly defined or inferred as Any.
Two forms:
arr[i]<optionalLeadingType> [ elem1, elem2, ... ]Element type is either explicitly defined or inferred as Any.
class <classname> (args?*) { block }
The main code block acts as the constructor for classes in LIA-lang.
Anything defined in the scope of a class becomes accessible from the class object.
Classes can be instantiated as so new <classname> (args?*).
Prefix type token followed by a parenthesized expression: <type> (expr)
import "<.lia/.llb/.dll path>" (as <identifier>)?
import "relative/or/absolute/path.lia"import "library.llb"import "library.dll"import "library.lia" as libSearch path: relative to current working directory. No cycle detection.
Once a scope is exited the scope and its contents are removed and freed from memory.
The free() function removes the inputted variable from the scope and frees the memory.
| Data Type | Allocation Location |
|---|---|
int | stack |
float | stack |
bool | stack |
null | stack |
void | stack |
string (< 32 bytes) | stack |
string (> 32 bytes) | heap |
| Arrays | heap |
| Objects | heap |
| Classes | heap |
LIA-lang is an interpreted programming language meaning execution is handled by a program that reads in the code and executes it based on its operation.
LIA uses a Tree‑walk interpreter that executes assembled LIA code.
LIA code is assembled into an abstract syntax tree and/or serialised into .ast file before execution or being bundled with an interpreter.
Runtime interpreters are either embedded into a compiled binary with assembled LIA bytecode or are separate and read in .ast or .lia files for execution.
In the case of a separate interpreter .lia files are parsed into unserialised .ast LIA bytecode before being executed
A separate interpreter will deserialise .ast files before executing them.
An embedded interpreter will deserialise the LIA bytecode appended to the end of its file after the "LIA\xFF" header before executing it.
Categories:
| Category | Examples | Handling (Typical) |
|---|---|---|
| Lexical | Invalid token | Abort parse; diagnostic emitted |
| Syntax | Unexpected token / structure | Abort parse with location |
| Type | Incompatible assignment | Diagnostic; may abort validation |
| Runtime (non‑fatal) | Missing library function | Report; attempt continue |
| Runtime (fatal) | Calling an undefined function | Terminate with error code |
Standard diagnostic format (illustrative):
<KIND>: <MESSAGE>
at <FILEPATH>:<LINE>
<OPTIONAL EXTRA CONTEXT>
Fatal runtime errors terminate the process with an implementation‑defined exit code. Non‑fatal runtime issues may be logged to stderr or console depending on runtime flags.
program ::= statement*
statement ::= function_decl
| var_decl
| if_stmt
| while_stmt
| return_stmt
| free_stmt
| import_stmt
| expression
function_decl ::= "function" type identifier "(" param_list? ")" block
param_list ::= param ("," param)*
param ::= type identifier
var_decl ::= "var" ("const")? type identifier ("=" expression)?
if_stmt ::= "if" "(" expression ")" block ("else" block)?
while_stmt ::= "while" "(" expression ")" block
return_stmt ::= "return" expression
free_stmt ::= "free" "(" ident_or_index ("," ident_or_index)* ")"
import_stmt ::= "import" string_literal
block ::= "{" statement* "}"
expression ::= assignment
assignment ::= binary ("=" assignment)?
binary ::= unary (op unary)* # op in precedence order (* /, + -, ==)
unary ::= ("+" | "-" | cast_prefix) unary | primary
cast_prefix ::= type "("
primary ::= number
| string_literal
| "true" | "false" | "null"
| identifier call_or_index?
| array_literal
| object_literal
| "(" expression ")"
call_or_index ::= "(" arg_list? ")" | "[" expression "]"
arg_list ::= expression ("," expression)*
array_literal ::= "[" type? (expression ("," expression)*)? "]"
object_literal ::= "{" object_field ("," object_field)* "}"
object_field ::= type identifier ":" expression
ident_or_index ::= identifier ("[" expression "]")?
type ::= "int" size? | "uint" size? | "float" size? | "char" size? | "string" | "bool" | "void" | "any"
size ::= integer_literal
++ --)* /)+ -)==)= right-associative).dll files can be loaded via import or the loadLibrary() native function
.dll public functions will be converted to work with LIA-lang placed in the global scope
.llb files can be loaded via import or the loadLibrary() native function and are essentially libraries specifically made for LIA-lang.
.llb files are technically just .dll files under the hood just with special functionality to interface with LIA-lang
The following native (built‑in) global functions are provided by the reference interpreter. Availability or exact behavior may vary in other runtimes.
| Function | Args | Returns | Description |
|---|---|---|---|
println | any* | void | Prints each argument on its own line (stringifies values). |
print | any* | void | Prints arguments without automatic newline separation. |
len | value | int | Length of string / array / object field count. |
push | array item index? | void | Appends an item to a specified or last item of an arry |
pop | array index? | void | Removes the specified or last item of an array |
input | (none) | string | Reads a line from standard input (newline trimmed). |
assert | bool | void | Panics if argument is false. |
exit | int? | (never) | Terminates process with optional integer code. |
ceil | number | int | Ceiling of numeric argument. |
sin | number | float | Sine (radians). |
cos | number | float | Cosine (radians). |
tan | number | float | Tangent (radians). |
log | base, value | float | Logarithm of value to given base. |
exp | value | float | e^value. |
random | (none) | float | Pseudo‑random float in [0,1]. |
randomInt | min, max | int | Pseudo‑random integer in inclusive range. |
sleep | ms | void | Sleeps current thread for milliseconds. |
date | (none) | string | Current date (YYYY‑MM‑DD) naive calculation. |
timestamp | (none) | int | Milliseconds since Unix epoch. |
timestampSeconds | (none) | int | Seconds since Unix epoch. |
loadLibrary | string | void | Loads a .dll or .llb library. |
| Header | Use |
|---|---|
LIA\xFF | Indicates the start of AST bytecode when embeded into an executable. |
0xAB | Magic number. |
0x01 | Version header (for future compatability) |
| Tag | Instruction | Data Structure |
|---|---|---|
0x01 | Program(instructions) | [number Of instructions][Instructions*] |
0x02 | Decloration(Identifier, Type, Value) | [Identifier][Type][Value] |
0x03 | Assignment(Identifier, Value) | [Identifier][Value] |
0x04 | If(condition, body, else_body) | [BinaryOp(condition)][Program(body)](else ? 1[Program(else_body)] : 0) |
0x05 | Function(Identifier, Type, args, body) | [Identifier][Type][length of args (as u32)][Argument(args)?*][Program(body)] |
0x06 | Argument(identifier, Type) | [string(identifier)][Type] |
0x07 | Call(identifier, args) | [string(identifier)][length of args (as u32)][Argument(args)?*] |
0x08 | Return(Value) | [Value] |
0x09 | BinaryOp(operation, left, right) | [string(operation)][AST(left)][AST(right)] |
0x0A | UnaryOp(operation, Value) | [string(operation)][Value] |
0x0B | Literal(Type, Value) | [Type][Value] |
0x0C | Identifier(identifier, index) | [string(identifier)](index ? 1[AST(index)] : 0) |
0x0D | Array(Type, items) | [Type][lenth of items (as u32)][Value(items)?*] |
0x0E | Object(fields| [length of fields]([Type][Identifier(indentifiers)][Value])?* | |
0x0F | While(condition, body) | [BinaryOp(condition)][Program(body)] |
0x10 | Free(Identifier) | [length of identifiers (as u32)][Identifier?*] |
0x11 | Class(Identifier, args, body) | [Identifier][length of args (as u32)][Argument(args)?*][Program(body)] |
0x12 | NewInstance(Identifier, args) | [Identifier][length of args (as u32)][Argument(args)?*] |
| Tag | Type | Data Structure |
|---|---|---|
0x01 | Int(signed, size) | [signed (0/1 | u8)][size (u8)] |
0x02 | Float(size) | [size (u8)] |
0x04 | String | NA |
0x05 | Bool | NA |
0x04 | Void | NA |
0x04 | Null | NA |
0x04 | Any | NA |
0x04 | Class(identifier) | [String(identifier)] |
| Type | Data Structure |
|---|---|
| Int(signed, size) | [<u/i><size>] |
| Float(size) | [f<size>] |
| Bool | [0/1] |
| String | [string length][string] |