Ovum’s source code uses a lexical syntax familiar to C-style and Kotlin-style languages:
Names for variables, functions, classes, etc., consist of letters, digits, and underscores, and must not begin with a digit. For example: myVar
, compute_sum
, GraphNode
. Identifiers are case-sensitive.
Naming Convention: Classes, functions, methods use PascalCase (e.g., Main
, ToString
, IsLess
). Keywords/modifiers remain lowercase (class
, interface
, var
, override
, pure
, unsafe
, etc.).
Ovum reserves certain words like fun
, class
, interface
, var
, override
, pure
, if
, else
, for
, while
, return
, unsafe
, val
, static
, public
, private
, implements
, as
, is
, null
, true
, false
, typealias
, destructor
, call
, etc. These cannot be used as identifiers.
42
, -17
) and floating-point numbers (e.g., 3.14
, -2.5
)true
, false
'A'
, '\n'
, '\t'
)"hello"
, "world"
) with escape sequences like "\n"
for newline, "\t"
for tab, "\\"
for backslash, "\""
for quote+
, -
, *
, /
, %
==
, !=
, <
, <=
, >
, >=
&&
(logical AND), ||
(logical OR), !
(negation), xor
(exclusive OR)=
(reference assignment), :=
(copy assignment)?.
(safe call), ?:
(Elvis)as
(cast), is
(type test),
(comma), ;
(semicolon), :
(colon), ()
(parentheses), {}
(braces), []
(brackets)::
//
and continue to the end of the line/*
and end with */
, can span multiple lines. Nested comments are not allowedSpaces, tabs, and newlines are generally ignored outside of separating tokens. Indentation is not significant (Ovum is not whitespace-sensitive except that newlines can terminate statements if no semicolon is present).
Core EBNF; whitespace/comments omitted. Operator precedence in §7.
Program ::= { Import | Conditional | GlobalDef } ;
Import ::= "#import" StringLiteral ;
Define ::= "#define" Identifier [ NumberLiteral ] ;
Undef ::= "#undef" Identifier ;
Conditional ::= "#ifdef" Identifier { GlobalDef | Import | Conditional }
[ "#else" { GlobalDef | Import | Conditional } ] "#endif"
| "#ifndef" Identifier { GlobalDef | Import | Conditional }
[ "#else" { GlobalDef | Import | Conditional } ] "#endif" ;
GlobalDef ::= FunctionDecl | ClassDecl | InterfaceDecl | GlobalVarDecl | TypeAliasDecl ;
FunctionDecl ::= [ "pure" ] "fun" Identifier "(" [ ParamList ] ")" [ ":" Type ] Block ;
ParamList ::= Parameter { "," Parameter } ;
Parameter ::= [ "var" ] Identifier ":" Type ;
ClassDecl ::= "class" Identifier [ "implements" TypeList ] ClassBody ;
TypeList ::= Type { "," Type } ;
ClassBody ::= "{" { ClassMember } "}" ;
ClassMember ::= FieldDecl | StaticFieldDecl | MethodDecl | DestructorDecl | CallDecl ;
FieldDecl ::= ( "private" | "public" ) ( "val" | "var" ) Identifier ":" Type [ "=" Expression ] ";" ;
StaticFieldDecl ::= "static" ( "private" | "public" ) ( "val" | "var" ) Identifier ":" Type [ "=" Expression ] ";" ;
MethodDecl ::= ( "private" | "public" ) [ "override" ] [ "pure" ]
"fun" Identifier "(" [ ParamList ] ")" [ ":" Type ] ( Block | ";" ) ;
CallDecl ::= ( "private" | "public" ) "call" "(" [ ParamList ] ")" [ ":" Type ] ( Block | ";" ) ;
DestructorDecl ::= ( "private" | "public" ) "destructor" "(" ")" ":" "Void" Block ;
InterfaceDecl ::= "interface" Identifier InterfaceBody ; // implicitly extends Object
InterfaceBody ::= "{" { InterfaceMember } "}" ;
InterfaceMember ::= InterfaceMethod | InterfaceCall ;
InterfaceMethod ::= "fun" Identifier "(" [ ParamList ] ")" [ ":" Type ] ";" ; // public & virtual
InterfaceCall ::= "call" "(" [ ParamList ] ")" [ ":" Type ] ";" ; // public & virtual
GlobalVarDecl ::= [ "var" ] Identifier ":" Type "=" Expression ";" ;
TypeAliasDecl ::= "typealias" Identifier "=" Type ";" ;
Type ::= NullableType | NonNullType ;
NullableType ::= NonNullType "?" ;
NonNullType ::= FundamentalType
| PrimitiveRefType
| "String"
| "IntArray" | "FloatArray" | "BoolArray" | "CharArray" | "ByteArray" | "PointerArray"
| "ObjectArray"
| "StringArray"
| Identifier ; // class/interface names (non-primitive)
FundamentalType ::= "int" | "float" | "bool" | "char" | "byte" | "pointer" ;
PrimitiveRefType ::= "Int" | "Float" | "Bool" | "Char" | "Byte" | "Pointer" ;
Block ::= "{" { Statement } "}" ;
Statement ::= VarDeclStmt | ExprStmt | ReturnStmt | IfStmt | WhileStmt | ForStmt | UnsafeStmt | Block ;
VarDeclStmt ::= [ "var" ] Identifier ":" Type "=" Expression ";" ;
ExprStmt ::= Expression ";" ;
ReturnStmt ::= "return" [ Expression ] ";" ;
IfStmt ::= "if" "(" Expression ")" Statement [ "else" Statement ] ;
WhileStmt ::= "while" "(" Expression ")" Statement ;
ForStmt ::= "for" "(" Identifier "in" Expression ")" Statement ;
UnsafeStmt ::= "unsafe" Block ;
Expression ::= Assignment ;
Assignment ::= ElvisExpr [ ("=" | ":=") Assignment ] ;
ElvisExpr ::= OrExpr [ "?:" ElvisExpr ] ; // right-assoc
OrExpr ::= AndExpr { "||" AndExpr } ;
AndExpr ::= XorExpr { "&&" XorExpr } ;
XorExpr ::= EqualityExpr { "xor" EqualityExpr } ;
EqualityExpr ::= RelExpr { ("==" | "!=") RelExpr } ;
RelExpr ::= AddExpr { ("<" | "<=" | ">" | ">=") AddExpr } ;
AddExpr ::= MulExpr { ("+" | "-") MulExpr } ;
MulExpr ::= UnaryExpr { ("*" | "/" | "%") UnaryExpr } ;
UnaryExpr ::= ("!" | "-" | "&" | "*") UnaryExpr
| Postfix ;
Postfix ::= Primary { PostfixOp } ;
PostfixOp ::= "." Identifier
| "." Identifier "(" [ ArgList ] ")"
| "(" [ ArgList ] ")" // function call or callable object call
| "as" Type // explicit cast; downcast yields nullable type
| "is" Type // type test → bool
| "?." Identifier [ "(" [ ArgList ] ")" ] // safe call chain
| "?." "(" [ ArgList ] ")" // safe callable object call
;
Primary ::= Identifier
| Literal
| "(" Expression ")"
| NamespaceRef
| FunctionLiteral ;
FunctionLiteral ::= "fun" "(" [ ParamList ] ")" [ ":" Type ] Block ;
NamespaceRef ::= Identifier "::" Identifier ;
ArgList ::= Expression { "," Expression } ;
Literal ::= NumberLiteral | StringLiteral | CharLiteral | "true" | "false" ;