Language Reference
Detailed reference for Jot grammar, precedence, values, scoping, and runtime behavior.
This page describes the current Jot language as implemented in parser/src/main/antlr4/Jot.g4 and the runtime/compiler modules.
Quick facts
- Jot programs are newline-separated statements.
- The language is expression-oriented (
if,fn, blocks, and lists are expressions). whileis a statement.- Functions are first-class values.
- Built-ins currently available by default:
print(value)andpush(list, value).
Lexical rules
Keywords
if, then, else, let, while, fn, true, false
Identifiers
[a-zA-Z_][a-zA-Z0-9_]*
Number literals
- Integer:
[0-9]+ - Decimal:
[0-9]+.[0-9]+
Notes:
- Negative literals are not a separate token; use expressions like
0 - 5. - String literals are not implemented.
- Comments are not implemented.
Program shape
A program must contain at least one statement.
- Newlines separate top-level statements.
- Optional blank lines are allowed before and after the program.
- No semicolon statement separator exists.
let a = 1
let b = 2
print(a + b)Statements
Jot statements:
letbinding- assignment
- block statement
whilestatement- expression statement
Let binding
let name = expressionRules:
- Variable must not already exist in the current scope.
- Shadowing is allowed in nested scopes.
Assignment
name = expressionRules:
- Variable must already exist in some visible scope.
- Assignment updates that slot.
Block statement
{
statement
statement
}As a statement, block value is discarded.
While statement
while condition statementThe body is any single statement. Use a block for multiple statements.
let i = 0
while i < 3 {
print(i)
i = i + 1
}Condition must evaluate to boolean at runtime.
Expression statement
Any expression can stand as a statement:
print(1)
fn(x) x + 1Its value is evaluated and ignored.
Expressions
Precedence and associativity
From lowest precedence to highest:
||(left-associative)&&(left-associative)==,!=(left-associative)<,<=,>,>=(left-associative)+,-(left-associative)*,/,%(left-associative)- Function call
(...)postfix (left-associative) - Primary expressions
Primary expressions
- Parenthesized expression:
(expr) - Block expression:
{ ... } - List expression:
[a, b, c] - If expression:
if cond then expr else expr - Function literal:
fn(p1, p2) expr - Boolean literal:
true,false - Number literal
- Identifier
Block expressions and block values
A block expression creates a new local scope.
let x = {
let y = 2
y + 3
}
print(x) # 5Rule for block value:
- If the last statement in the block is an expression statement, that expression is the block result.
- Otherwise the block result is
null.
print({ let y = 1 y })
print({ let y = 1 })Output:
1
nullIf expressions
Syntax:
if condition then trueBranch else falseBranchelsebranch is optional.- If
elseis omitted and condition is false, result isnull. - Condition must be boolean at runtime.
print(if true then 1 else 2)
print(if false then 1)Functions and calls
Function literal syntax:
fn(param1, param2) bodyExpression- Body is exactly one expression.
- Parameter names must be unique.
- Functions are values; bind with
let.
let add = fn(a, b) a + b
print(add(2, 3))Call syntax:
callee(arg1, arg2)- Calls are postfix, so chaining is valid:
f()(1). - Arity is checked at runtime.
- Calling a non-callable value raises a runtime error.
Closures
Functions capture outer variables when the function value is created.
let x = 10
let f = fn() x
x = 20
print(f())Current behavior prints 10.
Values and runtime types
Current runtime value kinds:
LongDoubleJotBigIntegerJotBigDecimalBooleanJotListJotFunctionJotNull(displayed asnull)
Numeric behavior
- Large integer literals that do not fit
longbecomeJotBigInteger. - Decimal literals are parsed through
BigDecimal; they usedoublewhen exactly representable and finite, otherwiseJotBigDecimal. - Numeric operators are defined for integer-family pairs and decimal-family pairs.
- Mixed
Long/Doublearithmetic is currently a type error.
Example:
print(1 + 2) # ok
print(1 + 2.0) # runtime type errorOperators
Arithmetic
+, -, *, /, %
Runtime type checks apply. Invalid operand combinations raise runtime errors.
Comparisons
<, <=, >, >=
Defined for numeric types.
Equality
==, !=
- Numbers and booleans compare by value.
null == nullistrue.- Other object types (for example lists/functions) compare by identity.
print([1, 2] == [1, 2])Output:
falseLogical operators
&&, ||
- Short-circuiting is implemented.
- Both operands must be booleans.
print(true || (1 / 0 == 0))
print(false && (1 / 0 == 0))Lists
List literal syntax:
[]
[1, 2, 3]
[1, true, fn(x) x]Lists are heterogeneous and mutable through push.
Built-ins
print(value)
Prints the value and returns null.
push(list, value)
Appends value to list and returns the same list.
let l = [1, 2]
print(push(l, 3))
print(l)Output:
[1, 2, 3]
[1, 2, 3]Scope rules
- New scopes are introduced by block expressions/statements and function bodies.
letchecks duplicates only in the current scope.- Inner scopes can shadow outer names.
let x = 1
{
let x = 2
print(x)
}
print(x)Output:
2
1Current limitations
Not implemented yet:
- Modules/import syntax
- String literals
- Comment syntax
- Type annotations in source syntax
- Top-level function declaration syntax (
fn name(...) ...)
Because there is no named function declaration form, direct self-recursive function definitions are not currently available in syntax.