We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
Don Giovanni
Jump to navigation
Jump to search
- This entry is not about Mozart's opera *Don Giovanni*.
Don Giovanni is designed by PSTF and his AI assistant. It is a Turing-complete language, that may be helpful to the design of Lingua Indeterminatum.
Syntax Overview
// Factorial (recursion)
fn factorial(n) {
if n <= 1 {
return 1;
} else {
return n * factorial(n - 1);
}
}
// Fibonacci (double recursion)
fn fib(n) {
if n <= 1 {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
// Higher-order function (closure)
fn make_adder(x) {
fn adder(y) {
return x + y;
}
return adder;
}
let add5 = make_adder(5);
print(add5(10)); // 15
// Loop (while) – ensures Turing completeness
let i = 0;
while i < 10 {
print(i);
i = i + 1;
}
print(factorial(5)); // 120
print(fib(10)); // 55
// Floats
let pi = 3.14159;
let radius = 5.0;
let area = pi * radius * radius;
print(area); // 78.53975
// Lists
let fruits = ["apple", "banana", "cherry"];
print(fruits[1]); // banana
fruits[1] = "blueberry";
print(fruits[1]); // blueberry
// Nested lists & indexing
let matrix = [[1, 2], [3, 4]];
print(matrix[0][1]); // 2
// Characters (with escapes)
let newline = '\n';
let tab = '\t';
print('A'); // A
print('Hello'[0]); // H (strings are indexable)
// Built-in functions: len() and push()
let numbers = [1, 2, 3];
print(len(numbers)); // 3
push(numbers, 4);
print(numbers[3]); // 4
// All mixed together
let mixed = [1, 2.5, "three", 'x'];
print(mixed[2]); // three
EBNF Definition
(* ------------------------------------------------------------------
DonGiovanni Programming Language – Complete EBNF Specification
------------------------------------------------------------------ *)
program = { statement } .
(* ----- Statements ------------------------------------------------- *)
statement = let_stmt
| assign_stmt
| if_stmt
| while_stmt
| return_stmt
| print_stmt
| function_def
| block
| expression ";" .
let_stmt = "let" identifier "=" expression ";" .
(* The left-hand side of an assignment must be an lvalue:
a simple variable or an indexed expression. *)
assign_stmt = lvalue "=" expression ";" .
lvalue = identifier
| expression "[" expression "]" .
if_stmt = "if" expression block [ "else" block ] .
while_stmt = "while" expression block .
return_stmt = "return" expression ";" .
print_stmt = "print" "(" expression ")" ";" .
function_def = "fn" identifier "(" [ identifier { "," identifier } ] ")" block .
block = "{" { statement } "}" .
(* ----- Expressions (precedence climbing) -------------------------- *)
expression = logical_or .
logical_or = logical_and { "||" logical_and } .
logical_and = comparison { "&&" comparison } .
comparison = additive { ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) additive } .
additive = multiplicative { ( "+" | "-" ) multiplicative } .
multiplicative = unary { ( "*" | "/" | "%" ) unary } .
unary = ( "-" | "!" ) unary
| postfix .
postfix = primary { "[" expression "]"
| "(" [ expression { "," expression } ] ")"
} .
(* ----- Primary expressions ---------------------------------------- *)
primary = integer_literal
| float_literal
| string_literal
| character_literal
| "true" | "false" | "nil"
| list_literal
| identifier
| "(" expression ")" .
list_literal = "[" [ expression { "," expression } ] "]" .
(* ----- Lexical tokens --------------------------------------------- *)
integer_literal = digit { digit } .
float_literal = digit { digit } "." digit { digit }
| "." digit { digit } .
string_literal = '"' { string_char | escape_sequence } '"' .
character_literal = "'" ( printable_char | escape_sequence ) "'" .
identifier = ( letter | "_" ) { letter | digit | "_" } .
(* Escapes are valid inside both strings and characters *)
escape_sequence = "\" ( "n" | "t" | "\\" | "\"" | "'" ) .
(* Comments are // to end of line; they are ignored by the lexer *)
comment = "//" { any_char - newline } newline .
(* Helper definitions (not part of the grammar proper) *)
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
letter = "A" | "B" | … | "Z" | "a" | "b" | … | "z" .
string_char = ? any character except backslash or double-quote ? .
printable_char = ? any character except backslash or single-quote ? .
any_char = ? any character ? .
newline = ? ASCII LF or CR/LF ? .
Interpreter in Python
import sys
# ----------------------------------------------------------------------
# 1. Lexer / Tokenizer (unchanged)
# ----------------------------------------------------------------------
class Token:
def __init__(self, type, value, line):
self.type = type
self.value = value
self.line = line
def __repr__(self):
return f"Token({self.type}, {repr(self.value)})"
class Lexer:
def __init__(self, source):
self.source = source
self.pos = 0
self.line = 1
self.tokens = []
self.scan()
def scan(self):
while self.pos < len(self.source):
c = self.source[self.pos]
if c in ' \t':
self.pos += 1
continue
if c == '\n':
self.line += 1
self.pos += 1
continue
if c == '/' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '/':
self.pos += 2
while self.pos < len(self.source) and self.source[self.pos] != '\n':
self.pos += 1
continue
if c.isdigit() or (c == '.' and self.pos + 1 < len(self.source) and self.source[self.pos+1].isdigit()):
num = ''
has_dot = False
while self.pos < len(self.source):
ch = self.source[self.pos]
if ch.isdigit():
num += ch
self.pos += 1
elif ch == '.' and not has_dot:
has_dot = True
num += ch
self.pos += 1
else:
break
if has_dot:
self.tokens.append(Token('FLOAT', float(num), self.line))
else:
self.tokens.append(Token('INT', int(num), self.line))
continue
if c.isalpha() or c == '_':
ident = ''
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
ident += self.source[self.pos]
self.pos += 1
keywords = {
'let': 'LET', 'fn': 'FN', 'if': 'IF', 'else': 'ELSE',
'while': 'WHILE', 'return': 'RETURN', 'print': 'PRINT',
'true': 'TRUE', 'false': 'FALSE', 'nil': 'NIL',
'len': 'LEN', 'push': 'PUSH'
}
tok_type = keywords.get(ident, 'IDENT')
value = ident
if tok_type == 'TRUE':
value = True
elif tok_type == 'FALSE':
value = False
elif tok_type == 'NIL':
value = None
self.tokens.append(Token(tok_type, value, self.line))
continue
if c == '"':
self.pos += 1
s = ''
while self.pos < len(self.source) and self.source[self.pos] != '"':
if self.source[self.pos] == '\\':
self.pos += 1
if self.pos < len(self.source):
esc = self.source[self.pos]
if esc == 'n': s += '\n'
elif esc == 't': s += '\t'
elif esc == '\\': s += '\\'
elif esc == '"': s += '"'
else: s += esc
self.pos += 1
else:
s += self.source[self.pos]
self.pos += 1
if self.pos < len(self.source):
self.pos += 1
self.tokens.append(Token('STRING', s, self.line))
continue
if c == "'":
self.pos += 1
ch = ''
if self.pos < len(self.source):
if self.source[self.pos] == '\\':
self.pos += 1
if self.pos < len(self.source):
esc = self.source[self.pos]
if esc == 'n': ch = '\n'
elif esc == 't': ch = '\t'
elif esc == '\\': ch = '\\'
elif esc == "'": ch = "'"
else: ch = esc
self.pos += 1
else:
ch = self.source[self.pos]
self.pos += 1
if self.pos < len(self.source) and self.source[self.pos] == "'":
self.pos += 1
else:
raise SyntaxError(f"Unterminated character at line {self.line}")
self.tokens.append(Token('CHAR', ch, self.line))
continue
if c == '=' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '=':
self.tokens.append(Token('EQEQ', '==', self.line))
self.pos += 2
continue
if c == '!' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '=':
self.tokens.append(Token('NEQ', '!=', self.line))
self.pos += 2
continue
if c == '<' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '=':
self.tokens.append(Token('LTE', '<=', self.line))
self.pos += 2
continue
if c == '>' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '=':
self.tokens.append(Token('GTE', '>=', self.line))
self.pos += 2
continue
if c == '&' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '&':
self.tokens.append(Token('AND', '&&', self.line))
self.pos += 2
continue
if c == '|' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '|':
self.tokens.append(Token('OR', '||', self.line))
self.pos += 2
continue
single = {
'=': 'EQ', '+': 'PLUS', '-': 'MINUS', '*': 'STAR',
'/': 'SLASH', '%': 'PERCENT', '(': 'LPAREN', ')': 'RPAREN',
'{': 'LBRACE', '}': 'RBRACE', ';': 'SEMICOLON',
'<': 'LT', '>': 'GT', '!': 'NOT',
',': 'COMMA', '[': 'LBRACKET', ']': 'RBRACKET'
}
if c in single:
self.tokens.append(Token(single[c], c, self.line))
self.pos += 1
continue
raise SyntaxError(f"Unexpected character '{c}' at line {self.line}")
self.tokens.append(Token('EOF', None, self.line))
# ----------------------------------------------------------------------
# 2. Parser (unchanged)
# ----------------------------------------------------------------------
class ASTNode:
pass
class Program(ASTNode):
def __init__(self, statements):
self.statements = statements
class LetStmt(ASTNode):
def __init__(self, name, expr):
self.name = name
self.expr = expr
class AssignStmt(ASTNode):
def __init__(self, target, expr):
self.target = target
self.expr = expr
class IfStmt(ASTNode):
def __init__(self, cond, then_block, else_block):
self.cond = cond
self.then_block = then_block
self.else_block = else_block
class WhileStmt(ASTNode):
def __init__(self, cond, body):
self.cond = cond
self.body = body
class ReturnStmt(ASTNode):
def __init__(self, expr):
self.expr = expr
class PrintStmt(ASTNode):
def __init__(self, expr):
self.expr = expr
class Block(ASTNode):
def __init__(self, statements):
self.statements = statements
class FunctionDef(ASTNode):
def __init__(self, name, params, body):
self.name = name
self.params = params
self.body = body
class BinaryExpr(ASTNode):
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
class UnaryExpr(ASTNode):
def __init__(self, op, expr):
self.op = op
self.expr = expr
class CallExpr(ASTNode):
def __init__(self, callee, args):
self.callee = callee
self.args = args
class IndexExpr(ASTNode):
def __init__(self, obj, index):
self.obj = obj
self.index = index
class VarExpr(ASTNode):
def __init__(self, name):
self.name = name
class IntLiteral(ASTNode):
def __init__(self, value):
self.value = value
class FloatLiteral(ASTNode):
def __init__(self, value):
self.value = value
class StringLiteral(ASTNode):
def __init__(self, value):
self.value = value
class CharLiteral(ASTNode):
def __init__(self, value):
self.value = value
class BoolLiteral(ASTNode):
def __init__(self, value):
self.value = value
class NilLiteral(ASTNode):
pass
class ListLiteral(ASTNode):
def __init__(self, elements):
self.elements = elements
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
def peek(self):
return self.tokens[self.pos]
def next_token(self):
tok = self.peek()
self.pos += 1
return tok
def expect(self, type, err=None):
if self.peek().type == type:
return self.next_token()
raise SyntaxError(f"Expected {type}, got {self.peek().type} at line {self.peek().line}")
def parse_program(self):
stmts = []
while self.peek().type != 'EOF':
stmts.append(self.parse_statement())
return Program(stmts)
def parse_statement(self):
tok = self.peek()
if tok.type == 'LET':
return self.parse_let()
if tok.type == 'FN':
return self.parse_function()
if tok.type == 'IF':
return self.parse_if()
if tok.type == 'WHILE':
return self.parse_while()
if tok.type == 'RETURN':
return self.parse_return()
if tok.type == 'PRINT':
return self.parse_print()
if tok.type == 'LBRACE':
return self.parse_block()
saved_pos = self.pos
try:
target = self.parse_expression()
if self.peek().type == 'EQ':
self.next_token()
expr = self.parse_expression()
self.expect('SEMICOLON')
if not isinstance(target, (VarExpr, IndexExpr)):
raise SyntaxError(f"Invalid assignment target at line {tok.line}")
return AssignStmt(target, expr)
except:
self.pos = saved_pos
expr = self.parse_expression()
self.expect('SEMICOLON')
return expr
def parse_let(self):
self.next_token()
name = self.expect('IDENT').value
self.expect('EQ')
expr = self.parse_expression()
self.expect('SEMICOLON')
return LetStmt(name, expr)
def parse_function(self):
self.next_token()
name = self.expect('IDENT').value
self.expect('LPAREN')
params = []
if self.peek().type != 'RPAREN':
params.append(self.expect('IDENT').value)
while self.peek().type == ',':
self.next_token()
params.append(self.expect('IDENT').value)
self.expect('RPAREN')
body = self.parse_block()
return FunctionDef(name, params, body)
def parse_block(self):
self.expect('LBRACE')
stmts = []
while self.peek().type != 'RBRACE' and self.peek().type != 'EOF':
stmts.append(self.parse_statement())
self.expect('RBRACE')
return Block(stmts)
def parse_if(self):
self.next_token()
cond = self.parse_expression()
then_block = self.parse_block()
else_block = None
if self.peek().type == 'ELSE':
self.next_token()
else_block = self.parse_block()
return IfStmt(cond, then_block, else_block)
def parse_while(self):
self.next_token()
cond = self.parse_expression()
body = self.parse_block()
return WhileStmt(cond, body)
def parse_return(self):
self.next_token()
expr = self.parse_expression()
self.expect('SEMICOLON')
return ReturnStmt(expr)
def parse_print(self):
self.next_token()
self.expect('LPAREN')
expr = self.parse_expression()
self.expect('RPAREN')
self.expect('SEMICOLON')
return PrintStmt(expr)
def parse_expression(self, min_prec=0):
left = self.parse_primary()
return self.parse_binary_op(left, min_prec)
def parse_binary_op(self, left, min_prec):
prec = self.get_precedence()
while prec >= min_prec:
op = self.next_token()
right = self.parse_expression(prec + 1)
left = BinaryExpr(left, op.value, right)
prec = self.get_precedence()
return left
def get_precedence(self):
tok = self.peek()
if tok.type in ('OR',): return 1
if tok.type in ('AND',): return 2
if tok.type in ('EQEQ', 'NEQ', 'LT', 'LTE', 'GT', 'GTE'): return 3
if tok.type in ('PLUS', 'MINUS'): return 4
if tok.type in ('STAR', 'SLASH', 'PERCENT'): return 5
return -1
def parse_primary(self):
tok = self.peek()
if tok.type == 'INT':
self.next_token()
return IntLiteral(tok.value)
if tok.type == 'FLOAT':
self.next_token()
return FloatLiteral(tok.value)
if tok.type == 'STRING':
self.next_token()
return StringLiteral(tok.value)
if tok.type == 'CHAR':
self.next_token()
return CharLiteral(tok.value)
if tok.type == 'TRUE':
self.next_token()
return BoolLiteral(True)
if tok.type == 'FALSE':
self.next_token()
return BoolLiteral(False)
if tok.type == 'NIL':
self.next_token()
return NilLiteral()
if tok.type == 'LBRACKET':
self.next_token()
elems = []
if self.peek().type != 'RBRACKET':
elems.append(self.parse_expression())
while self.peek().type == ',':
self.next_token()
elems.append(self.parse_expression())
self.expect('RBRACKET')
return ListLiteral(elems)
if tok.type == 'LPAREN':
self.next_token()
expr = self.parse_expression()
self.expect('RPAREN')
return self.parse_postfix(expr)
if tok.type == 'IDENT' or tok.type in ('LEN', 'PUSH'):
self.next_token()
ident = tok.value
expr = VarExpr(ident)
return self.parse_postfix(expr)
if tok.type in ('NOT', 'MINUS'):
op = self.next_token()
expr = self.parse_primary()
return UnaryExpr(op.value, expr)
raise SyntaxError(f"Unexpected token {tok.type} at line {tok.line}")
def parse_postfix(self, left):
while True:
tok = self.peek()
if tok.type == 'LBRACKET':
self.next_token()
idx = self.parse_expression()
self.expect('RBRACKET')
left = IndexExpr(left, idx)
elif tok.type == 'LPAREN':
self.next_token()
args = []
if self.peek().type != 'RPAREN':
args.append(self.parse_expression())
while self.peek().type == ',':
self.next_token()
args.append(self.parse_expression())
self.expect('RPAREN')
left = CallExpr(left, args)
else:
break
return left
# ----------------------------------------------------------------------
# 3. Interpreter (UPDATED with read built-ins)
# ----------------------------------------------------------------------
class ReturnException(Exception):
def __init__(self, value):
self.value = value
class Function:
def __init__(self, name, params, body, env):
self.name = name
self.params = params
self.body = body
self.env = env
def call(self, args, interpreter):
if len(args) != len(self.params):
raise RuntimeError(f"Function {self.name} expects {len(self.params)} args, got {len(args)}")
new_env = Environment(outer=self.env)
for i, param in enumerate(self.params):
new_env.set(param, args[i])
try:
interpreter.execute_block(self.body.statements, new_env)
except ReturnException as ret:
return ret.value
return None
class Environment:
def __init__(self, outer=None):
self.store = {}
self.outer = outer
def get(self, name):
if name in self.store:
return self.store[name]
if self.outer:
return self.outer.get(name)
raise RuntimeError(f"Undefined variable '{name}'")
def set(self, name, value):
self.store[name] = value
# ---- NEW BUILT-INS FOR INPUT ----
class BuiltinRead:
def call(self, args):
if len(args) != 0:
raise RuntimeError("read() expects 0 arguments")
try:
return sys.stdin.readline().rstrip('\n')
except:
return None
class BuiltinReadInt:
def call(self, args):
if len(args) != 0:
raise RuntimeError("read_int() expects 0 arguments")
try:
line = sys.stdin.readline().rstrip('\n')
return int(line)
except ValueError:
raise RuntimeError("read_int(): expected an integer")
except:
return None
class BuiltinReadFloat:
def call(self, args):
if len(args) != 0:
raise RuntimeError("read_float() expects 0 arguments")
try:
line = sys.stdin.readline().rstrip('\n')
return float(line)
except ValueError:
raise RuntimeError("read_float(): expected a number")
except:
return None
class BuiltinLen:
def call(self, args):
if len(args) != 1:
raise RuntimeError("len() expects exactly 1 argument")
obj = args[0]
if isinstance(obj, (str, list)):
return len(obj)
raise RuntimeError("len() only works on strings and lists")
class BuiltinPush:
def call(self, args):
if len(args) != 2:
raise RuntimeError("push() expects exactly 2 arguments")
lst = args[0]
val = args[1]
if not isinstance(lst, list):
raise RuntimeError("push() first argument must be a list")
lst.append(val)
return None
class Interpreter:
def __init__(self):
self.global_env = Environment()
self.global_env.set('len', BuiltinLen())
self.global_env.set('push', BuiltinPush())
# Register input built-ins
self.global_env.set('read', BuiltinRead())
self.global_env.set('read_int', BuiltinReadInt())
self.global_env.set('read_float', BuiltinReadFloat())
def interpret(self, program):
try:
self.execute_program(program, self.global_env)
except RuntimeError as e:
print(f"Runtime Error: {e}")
def execute_program(self, program, env):
for stmt in program.statements:
self.execute(stmt, env)
def execute(self, stmt, env):
if isinstance(stmt, LetStmt):
val = self.evaluate(stmt.expr, env)
env.set(stmt.name, val)
elif isinstance(stmt, AssignStmt):
target = stmt.target
val = self.evaluate(stmt.expr, env)
if isinstance(target, VarExpr):
self.set_var(target.name, val, env)
elif isinstance(target, IndexExpr):
obj = self.evaluate(target.obj, env)
idx = self.evaluate(target.index, env)
if not isinstance(obj, list):
raise RuntimeError("Can only index into lists")
if not isinstance(idx, int):
raise RuntimeError("List index must be integer")
if idx < 0 or idx >= len(obj):
raise RuntimeError(f"Index {idx} out of range")
obj[idx] = val
else:
raise RuntimeError("Invalid assignment target")
elif isinstance(stmt, FunctionDef):
func = Function(stmt.name, stmt.params, stmt.body, env)
env.set(stmt.name, func)
elif isinstance(stmt, IfStmt):
cond = self.evaluate(stmt.cond, env)
if self.is_truthy(cond):
self.execute_block(stmt.then_block.statements, env)
elif stmt.else_block:
self.execute_block(stmt.else_block.statements, env)
elif isinstance(stmt, WhileStmt):
while self.is_truthy(self.evaluate(stmt.cond, env)):
self.execute_block(stmt.body.statements, env)
elif isinstance(stmt, ReturnStmt):
val = self.evaluate(stmt.expr, env)
raise ReturnException(val)
elif isinstance(stmt, PrintStmt):
val = self.evaluate(stmt.expr, env)
print(self.stringify(val), end='')
print()
elif isinstance(stmt, Block):
self.execute_block(stmt.statements, env)
else:
self.evaluate(stmt, env)
def set_var(self, name, value, env):
if name in env.store:
env.set(name, value)
return
if env.outer:
self.set_var(name, value, env.outer)
return
raise RuntimeError(f"Undefined variable '{name}'")
def execute_block(self, statements, env):
for stmt in statements:
self.execute(stmt, env)
def evaluate(self, expr, env):
if isinstance(expr, IntLiteral):
return expr.value
if isinstance(expr, FloatLiteral):
return expr.value
if isinstance(expr, StringLiteral):
return expr.value
if isinstance(expr, CharLiteral):
return expr.value
if isinstance(expr, BoolLiteral):
return expr.value
if isinstance(expr, NilLiteral):
return None
if isinstance(expr, ListLiteral):
return [self.evaluate(e, env) for e in expr.elements]
if isinstance(expr, VarExpr):
return env.get(expr.name)
if isinstance(expr, UnaryExpr):
right = self.evaluate(expr.expr, env)
if expr.op == '-':
return -right
if expr.op == '!':
return not self.is_truthy(right)
if isinstance(expr, BinaryExpr):
left = self.evaluate(expr.left, env)
right = self.evaluate(expr.right, env)
op = expr.op
if op == '+': return left + right
if op == '-': return left - right
if op == '*': return left * right
if op == '/':
if right == 0: raise RuntimeError("Division by zero")
return left / right
if op == '%':
if right == 0: raise RuntimeError("Modulo by zero")
return left % right
if op == '==': return left == right
if op == '!=': return left != right
if op == '<': return left < right
if op == '<=': return left <= right
if op == '>': return left > right
if op == '>=': return left >= right
if op == '&&': return self.is_truthy(left) and self.is_truthy(right)
if op == '||': return self.is_truthy(left) or self.is_truthy(right)
if isinstance(expr, IndexExpr):
obj = self.evaluate(expr.obj, env)
idx = self.evaluate(expr.index, env)
if isinstance(obj, str):
if not isinstance(idx, int):
raise RuntimeError("String index must be integer")
if idx < 0 or idx >= len(obj):
raise RuntimeError(f"Index {idx} out of range")
return obj[idx]
if not isinstance(obj, list):
raise RuntimeError("Can only index into lists or strings")
if not isinstance(idx, int):
raise RuntimeError("Index must be integer")
if idx < 0 or idx >= len(obj):
raise RuntimeError(f"Index {idx} out of range")
return obj[idx]
if isinstance(expr, CallExpr):
callee = self.evaluate(expr.callee, env)
args = [self.evaluate(arg, env) for arg in expr.args]
# Check built-ins first (including new input ones)
if isinstance(callee, (BuiltinLen, BuiltinPush, BuiltinRead, BuiltinReadInt, BuiltinReadFloat)):
return callee.call(args)
if isinstance(callee, Function):
return callee.call(args, self)
raise RuntimeError(f"Can only call functions or built-ins")
raise RuntimeError(f"Unknown expression type {type(expr)}")
def is_truthy(self, val):
if val is None:
return False
if isinstance(val, bool):
return val
if isinstance(val, (int, float)):
return val != 0
if isinstance(val, str):
return val != ""
return True
def stringify(self, val):
if val is None:
return "nil"
if isinstance(val, bool):
return "true" if val else "false"
if isinstance(val, str):
return val
if isinstance(val, list):
return "[" + ", ".join(self.stringify(v) for v in val) + "]"
return str(val)
# ----------------------------------------------------------------------
# 4. Main
# ----------------------------------------------------------------------
def run_source(source):
lexer = Lexer(source)
parser = Parser(lexer.tokens)
ast = parser.parse_program()
interpreter = Interpreter()
interpreter.interpret(ast)
if __name__ == "__main__":
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
run_source(f.read())
else:
print("DonGiovanni REPL (type 'exit()' to quit)")
interpreter = Interpreter()
while True:
try:
line = input(">>> ")
if line.strip() == "exit()":
break
lexer = Lexer(line + "\n")
parser = Parser(lexer.tokens)
ast = parser.parse_program()
interpreter.interpret(ast)
except Exception as e:
print(f"Error: {e}")
Example
Factorial
Shown above.
Hello, World!
print("Hello, World!")
A+B Problem
// A+B: function that adds two numbers and prints the result
fn add(a, b) {
return a + b;
}
let result = add(5, 7);
print(result); // 12
// Or directly:
print(add(3.5, 2.7)); // 6.2 (floats work too)
Prime Number Detector
fn is_prime(n) {
if n < 2 {
return false;
}
let i = 2;
while i * i <= n {
if n % i == 0 {
return false;
}
i = i + 1;
}
return true;
}
print(is_prime(17)); // true
print(is_prime(18)); // false
// Print all primes up to 30
let x = 2;
while x <= 30 {
if is_prime(x) {
print(x);
}
x = x + 1;
}
// Output: 2 3 5 7 11 13 17 19 23 29
Greeting
print("What is your name?");
let name = read();
print("Hello, " + name + "!");
See Also
- Minimialized Programming Language by the same author
- MØSS by the same author