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.
FlamePL/Python Interpreter
Jump to navigation
Jump to search
import re
import sys
import math
from decimal import Decimal, getcontext
# Set high precision for arbitrary‑precision floats
getcontext().prec = 80
# ----------------------------------------------------------------------
# Data types
# ----------------------------------------------------------------------
class FlamePLType:
"""First‑class type objects."""
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
INT_TYPE = FlamePLType("int")
FLOAT_TYPE = FlamePLType("float")
COMPLEX_TYPE= FlamePLType("complex")
LIST_TYPE = FlamePLType("list")
PAIR_TYPE = FlamePLType("pair")
DICT_TYPE = FlamePLType("dict")
STR_TYPE = FlamePLType("str")
NULL_TYPE = FlamePLType("null")
FUNCTION_TYPE= FlamePLType("function")
CLASS_TYPE = FlamePLType("class")
BOOL_TYPE = FlamePLType("bool") # <-- NEW boolean type
class FlamePLComplex:
"""Arbitrary‑precision complex number (real, imag as Decimal)."""
def __init__(self, real, imag):
self.real = Decimal(real)
self.imag = Decimal(imag)
def __repr__(self):
if self.imag < 0:
return f"{self.real} - {abs(self.imag)}i"
return f"{self.real} + {self.imag}i"
def __add__(self, other):
if isinstance(other, FlamePLComplex):
return FlamePLComplex(self.real + other.real, self.imag + other.imag)
return FlamePLComplex(self.real + Decimal(other), self.imag)
def __sub__(self, other):
if isinstance(other, FlamePLComplex):
return FlamePLComplex(self.real - other.real, self.imag - other.imag)
return FlamePLComplex(self.real - Decimal(other), self.imag)
def __mul__(self, other):
if isinstance(other, FlamePLComplex):
r = self.real * other.real - self.imag * other.imag
i = self.real * other.imag + self.imag * other.real
return FlamePLComplex(r, i)
return FlamePLComplex(self.real * Decimal(other), self.imag * Decimal(other))
def __truediv__(self, other):
if isinstance(other, FlamePLComplex):
denom = other.real**2 + other.imag**2
r = (self.real * other.real + self.imag * other.imag) / denom
i = (self.imag * other.real - self.real * other.imag) / denom
return FlamePLComplex(r, i)
return FlamePLComplex(self.real / Decimal(other), self.imag / Decimal(other))
def __eq__(self, other):
if not isinstance(other, FlamePLComplex):
return False
return self.real == other.real and self.imag == other.imag
class FlamePLPair:
"""Ordered pair."""
def __init__(self, first, second):
self.first = first
self.second = second
def __repr__(self):
return f"({self.first}, {self.second})"
# ----------------------------------------------------------------------
# Environment (for variable lookup & closures)
# ----------------------------------------------------------------------
class Environment:
def __init__(self, outer=None):
self.outer = outer
self.data = {}
def get(self, name):
if name in self.data:
return self.data[name]
if self.outer:
return self.outer.get(name)
raise NameError(f"Undefined variable: {name}")
def set(self, name, value):
self.data[name] = value
def define(self, name, value):
self.data[name] = value
def extend(self):
return Environment(self)
# ----------------------------------------------------------------------
# AST nodes
# ----------------------------------------------------------------------
class ASTNode:
pass
class Number(ASTNode):
def __init__(self, value):
self.value = Decimal(value) if isinstance(value, (int, float, str)) else value
class ImagLiteral(ASTNode):
def __init__(self, value):
self.value = Decimal(value)
class String(ASTNode):
def __init__(self, value):
self.value = value
class BooleanLiteral(ASTNode): # <-- NEW node for true/false
def __init__(self, value):
self.value = value # True or False
class ListLiteral(ASTNode):
def __init__(self, elements):
self.elements = elements
class PairLiteral(ASTNode):
def __init__(self, first, second):
self.first = first
self.second = second
class DictLiteral(ASTNode):
def __init__(self, items):
self.items = items
class NullLiteral(ASTNode):
pass
class Var(ASTNode):
def __init__(self, name):
self.name = name
class BinaryOp(ASTNode):
def __init__(self, op, left, right):
self.op = op
self.left = left
self.right = right
class UnaryOp(ASTNode):
def __init__(self, op, expr):
self.op = op
self.expr = expr
class Assign(ASTNode):
def __init__(self, name, expr):
self.name = name
self.expr = expr
class If(ASTNode):
def __init__(self, cond, then_block, else_block):
self.cond = cond
self.then_block = then_block
self.else_block = else_block
class While(ASTNode):
def __init__(self, cond, body):
self.cond = cond
self.body = body
class Return(ASTNode):
def __init__(self, expr):
self.expr = expr
class FunctionDef(ASTNode):
def __init__(self, name, params, body):
self.name = name
self.params = params
self.body = body
class LambdaDef(ASTNode):
def __init__(self, params, body):
self.params = params
self.body = body
class ClassDef(ASTNode):
def __init__(self, name, parent_name, methods):
self.name = name
self.parent_name = parent_name
self.methods = methods
class Call(ASTNode):
def __init__(self, func, args):
self.func = func
self.args = args
class MethodCall(ASTNode):
def __init__(self, obj, method, args):
self.obj = obj
self.method = method
self.args = args
class Index(ASTNode):
def __init__(self, obj, index):
self.obj = obj
self.index = index
class Print(ASTNode):
def __init__(self, expr):
self.expr = expr
class Input(ASTNode):
def __init__(self, prompt):
self.prompt = prompt
class TypeOf(ASTNode):
def __init__(self, expr):
self.expr = expr
class Convert(ASTNode):
def __init__(self, kind, expr):
self.kind = kind
self.expr = expr
# ----------------------------------------------------------------------
# Callable objects
# ----------------------------------------------------------------------
class FlamePLFunction:
def __init__(self, params, body, env, name=None):
self.params = params
self.body = body
self.env = env
self.name = name or "<lambda>"
def call(self, args, evaluator):
if len(args) != len(self.params):
raise TypeError(f"{self.name} expects {len(self.params)} args, got {len(args)}")
new_env = Environment(self.env)
for p, a in zip(self.params, args):
new_env.define(p, a)
try:
evaluator.evaluate_block(self.body, new_env)
except ReturnException as e:
return e.value
return None
class FlamePLBoundMethod:
def __init__(self, obj, method_fn):
self.obj = obj
self.method_fn = method_fn
def call(self, args, evaluator):
return self.method_fn.call([self.obj] + args, evaluator)
class FlamePLClass:
def __init__(self, name, methods, parent=None):
self.name = name
self.methods = methods
self.parent = parent
def call(self, args, evaluator):
instance = FlamePLInstance(self)
init = self.get_method("init")
if init:
init.call([instance] + args, evaluator)
return instance
def get_method(self, name):
if name in self.methods:
return self.methods[name]
if self.parent:
return self.parent.get_method(name)
return None
class FlamePLInstance:
def __init__(self, klass):
self.klass = klass
self.fields = {}
def get_method(self, name):
method_fn = self.klass.get_method(name)
if method_fn:
return FlamePLBoundMethod(self, method_fn)
return None
# ----------------------------------------------------------------------
# Exception for return
# ----------------------------------------------------------------------
class ReturnException(Exception):
def __init__(self, value):
self.value = value
# ----------------------------------------------------------------------
# Lexer
# ----------------------------------------------------------------------
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 = []
def tokenize(self):
while self.pos < len(self.source):
ch = self.source[self.pos]
if ch in ' \t\r':
self.pos += 1
continue
if ch == '\n':
self.line += 1
self.pos += 1
continue
# Comments
if ch == '#':
self.pos += 1
while self.pos < len(self.source) and self.source[self.pos] != '\n':
self.pos += 1
continue
# Multi‑line string
if ch == '"' and self.pos + 2 < len(self.source) and self.source[self.pos:self.pos+3] == '"""':
self.pos += 3
start_line = self.line
start_pos = self.pos
while self.pos < len(self.source):
if self.source[self.pos:self.pos+3] == '"""':
self.pos += 3
content = self.source[start_pos:self.pos-3]
self.tokens.append(Token('STRING', content, start_line))
break
if self.source[self.pos] == '\n':
self.line += 1
self.pos += 1
continue
# Single‑line string
if ch == '"':
self.pos += 1
start_pos = self.pos
while self.pos < len(self.source) and self.source[self.pos] != '"':
if self.source[self.pos] == '\n':
self.line += 1
self.pos += 1
if self.pos >= len(self.source):
raise SyntaxError("Unterminated string")
content = self.source[start_pos:self.pos]
self.pos += 1
self.tokens.append(Token('STRING', content, self.line))
continue
# Assignment <-
if ch == '<' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '-':
self.tokens.append(Token('ASSIGN', '<-', self.line))
self.pos += 2
continue
# Integer division //
if ch == '/' and self.pos + 1 < len(self.source) and self.source[self.pos+1] == '/':
self.tokens.append(Token('OP', '//', self.line))
self.pos += 2
continue
# Exponent ^
if ch == '^':
self.tokens.append(Token('OP', '^', self.line))
self.pos += 1
continue
# Imaginary literal: e.g. 4i, 10i
imag_match = re.match(r'^(\d+)i', self.source[self.pos:])
if imag_match:
val = imag_match.group(1)
self.tokens.append(Token('IMAG', val, self.line))
self.pos += len(imag_match.group(0))
continue
# Number (float or int)
num_match = re.match(r'^(\d+\.\d+|\d+)', self.source[self.pos:])
if num_match:
val = num_match.group(1)
self.tokens.append(Token('NUMBER', val, self.line))
self.pos += len(num_match.group(0))
continue
# Operators and delimiters
if ch in '+-*/=(){}[],:<>':
if ch == '=':
self.tokens.append(Token('OP', '=', self.line))
elif ch == '<':
self.tokens.append(Token('OP', '<', self.line))
elif ch == '>':
self.tokens.append(Token('OP', '>', self.line))
else:
self.tokens.append(Token('OP', ch, self.line))
self.pos += 1
continue
# Keywords and identifiers
ident_match = re.match(r'^([A-Za-z_][A-Za-z0-9_]*)', self.source[self.pos:])
if ident_match:
word = ident_match.group(1)
keywords = {
'fn', 'class', 'if', 'then', 'else', 'end', 'while', 'do',
'return', 'null', 'lambda', 'and', 'or', 'not',
'true', 'false' # <-- booleans as keywords
}
if word in keywords:
self.tokens.append(Token('KEYWORD', word, self.line))
else:
self.tokens.append(Token('IDENTIFIER', word, self.line))
self.pos += len(word)
continue
raise SyntaxError(f"Unexpected char '{ch}' at line {self.line}")
self.tokens.append(Token('EOF', None, self.line))
return self.tokens
# ----------------------------------------------------------------------
# Parser
# ----------------------------------------------------------------------
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
def peek(self):
return self.tokens[self.pos]
def consume(self, expected_type=None, expected_value=None):
tok = self.peek()
if expected_type is not None and tok.type != expected_type:
raise SyntaxError(f"Expected {expected_type}, got {tok.type} at line {tok.line}")
if expected_value is not None and tok.value != expected_value:
raise SyntaxError(f"Expected '{expected_value}', got '{tok.value}' at line {tok.line}")
self.pos += 1
return tok
def match(self, type_, value=None):
tok = self.peek()
if tok.type == type_ and (value is None or tok.value == value):
self.pos += 1
return True
return False
def parse(self):
statements = []
while not self.match('EOF'):
statements.append(self.parse_statement())
return statements
def parse_statement(self):
tok = self.peek()
if tok.type == 'KEYWORD':
if tok.value == 'fn':
return self.parse_function_def()
elif tok.value == 'class':
return self.parse_class_def()
elif tok.value == 'if':
return self.parse_if()
elif tok.value == 'while':
return self.parse_while()
elif tok.value == 'return':
self.consume('KEYWORD', 'return')
expr = self.parse_expression()
return Return(expr)
elif tok.value == 'print':
self.consume('KEYWORD', 'print')
self.consume('OP', '(')
expr = self.parse_expression()
self.consume('OP', ')')
return Print(expr)
elif tok.value == 'input':
self.consume('KEYWORD', 'input')
self.consume('OP', '(')
prompt = self.parse_expression()
self.consume('OP', ')')
return Input(prompt)
# Assignment or expression
left = self.parse_expression()
if self.match('ASSIGN'):
if not isinstance(left, Var):
raise SyntaxError("Left side of assignment must be a variable")
right = self.parse_expression()
return Assign(left.name, right)
return left
def parse_function_def(self):
self.consume('KEYWORD', 'fn')
name_tok = self.consume('IDENTIFIER')
self.consume('OP', '(')
params = []
if not self.match('OP', ')'):
while True:
p = self.consume('IDENTIFIER')
params.append(p.value)
if self.match('OP', ','):
continue
self.consume('OP', ')')
break
body = self.parse_block()
return FunctionDef(name_tok.value, params, body)
def parse_lambda(self):
self.consume('KEYWORD', 'lambda')
self.consume('OP', '(')
params = []
if not self.match('OP', ')'):
while True:
p = self.consume('IDENTIFIER')
params.append(p.value)
if self.match('OP', ','):
continue
self.consume('OP', ')')
break
self.consume('OP', '->')
body = self.parse_expression()
return LambdaDef(params, [body])
def parse_class_def(self):
self.consume('KEYWORD', 'class')
name_tok = self.consume('IDENTIFIER')
parent_name = None
if self.match('OP', ':'):
parent_tok = self.consume('IDENTIFIER')
parent_name = parent_tok.value
methods = {}
while True:
tok = self.peek()
if tok.type == 'KEYWORD' and tok.value == 'end':
self.consume('KEYWORD', 'end')
break
if tok.type == 'KEYWORD' and tok.value == 'fn':
fn_def = self.parse_function_def()
methods[fn_def.name] = fn_def
else:
raise SyntaxError(f"Expected method definition inside class, got {tok}")
return ClassDef(name_tok.value, parent_name, methods)
def parse_block(self):
stmts = []
while True:
tok = self.peek()
if tok.type == 'KEYWORD' and tok.value in ('end', 'else'):
break
if tok.type == 'EOF':
break
stmts.append(self.parse_statement())
return stmts
def parse_if(self):
self.consume('KEYWORD', 'if')
cond = self.parse_expression()
self.consume('KEYWORD', 'then')
then_block = []
while True:
tok = self.peek()
if tok.type == 'KEYWORD' and tok.value in ('else', 'end'):
break
then_block.append(self.parse_statement())
else_block = []
if self.match('KEYWORD', 'else'):
while True:
tok = self.peek()
if tok.type == 'KEYWORD' and tok.value == 'end':
break
else_block.append(self.parse_statement())
self.consume('KEYWORD', 'end')
return If(cond, then_block, else_block)
def parse_while(self):
self.consume('KEYWORD', 'while')
cond = self.parse_expression()
self.consume('KEYWORD', 'do')
body = []
while True:
tok = self.peek()
if tok.type == 'KEYWORD' and tok.value == 'end':
break
body.append(self.parse_statement())
self.consume('KEYWORD', 'end')
return While(cond, body)
def parse_expression(self):
return self.parse_or()
def parse_or(self):
left = self.parse_and()
while self.match('KEYWORD', 'or'):
right = self.parse_and()
left = BinaryOp('or', left, right)
return left
def parse_and(self):
left = self.parse_not()
while self.match('KEYWORD', 'and'):
right = self.parse_not()
left = BinaryOp('and', left, right)
return left
def parse_not(self):
if self.match('KEYWORD', 'not'):
expr = self.parse_not()
return UnaryOp('not', expr)
return self.parse_comparison()
def parse_comparison(self):
left = self.parse_additive()
if self.match('OP', '=') or self.match('OP', '<') or self.match('OP', '>'):
op = self.tokens[self.pos-1].value
right = self.parse_additive()
return BinaryOp(op, left, right)
return left
def parse_additive(self):
left = self.parse_multiplicative()
while self.match('OP', '+') or self.match('OP', '-'):
op = self.tokens[self.pos-1].value
right = self.parse_multiplicative()
left = BinaryOp(op, left, right)
return left
def parse_multiplicative(self):
left = self.parse_power()
while self.match('OP', '*') or self.match('OP', '/') or self.match('OP', '//'):
op = self.tokens[self.pos-1].value
right = self.parse_power()
left = BinaryOp(op, left, right)
return left
def parse_power(self):
left = self.parse_unary()
if self.match('OP', '^'):
right = self.parse_power()
return BinaryOp('^', left, right)
return left
def parse_unary(self):
if self.match('OP', '-') or self.match('OP', '+'):
op = self.tokens[self.pos-1].value
expr = self.parse_unary()
return UnaryOp(op, expr)
return self.parse_postfix()
def parse_postfix(self):
expr = self.parse_atom()
while True:
if self.match('OP', '['):
idx = self.parse_expression()
self.consume('OP', ']')
expr = Index(expr, idx)
elif self.match('OP', '('):
args = []
if not self.match('OP', ')'):
while True:
args.append(self.parse_expression())
if self.match('OP', ','):
continue
self.consume('OP', ')')
break
expr = Call(expr, args)
elif self.match('OP', '.'):
method_tok = self.consume('IDENTIFIER')
self.consume('OP', '(')
args = []
if not self.match('OP', ')'):
while True:
args.append(self.parse_expression())
if self.match('OP', ','):
continue
self.consume('OP', ')')
break
expr = MethodCall(expr, method_tok.value, args)
else:
break
return expr
def parse_atom(self):
tok = self.peek()
if tok.type == 'NUMBER':
self.consume('NUMBER')
return Number(tok.value)
if tok.type == 'IMAG':
self.consume('IMAG')
return ImagLiteral(tok.value)
if tok.type == 'STRING':
self.consume('STRING')
return String(tok.value)
if tok.type == 'KEYWORD' and tok.value in ('true', 'false'): # <-- handle booleans
self.consume('KEYWORD')
return BooleanLiteral(tok.value == 'true')
if tok.type == 'IDENTIFIER':
self.consume('IDENTIFIER')
if tok.value == 'null':
return NullLiteral()
if tok.value == 'to_int':
self.consume('OP', '(')
expr = self.parse_expression()
self.consume('OP', ')')
return Convert('int', expr)
if tok.value == 'to_float':
self.consume('OP', '(')
expr = self.parse_expression()
self.consume('OP', ')')
return Convert('float', expr)
if tok.value == 'to_str':
self.consume('OP', '(')
expr = self.parse_expression()
self.consume('OP', ')')
return Convert('str', expr)
if tok.value == 'type_of':
self.consume('OP', '(')
expr = self.parse_expression()
self.consume('OP', ')')
return TypeOf(expr)
return Var(tok.value)
if tok.type == 'OP' and tok.value == '(':
self.consume('OP', '(')
expr = self.parse_expression()
if self.match('OP', ','):
second = self.parse_expression()
self.consume('OP', ')')
return PairLiteral(expr, second)
self.consume('OP', ')')
return expr
if tok.type == 'OP' and tok.value == '[':
self.consume('OP', '[')
elements = []
if not self.match('OP', ']'):
while True:
elements.append(self.parse_expression())
if self.match('OP', ','):
continue
self.consume('OP', ']')
break
return ListLiteral(elements)
if tok.type == 'OP' and tok.value == '{':
self.consume('OP', '{')
items = []
if not self.match('OP', '}'):
while True:
key = self.parse_expression()
self.consume('OP', ':')
value = self.parse_expression()
items.append((key, value))
if self.match('OP', ','):
continue
self.consume('OP', '}')
break
return DictLiteral(items)
if tok.type == 'KEYWORD' and tok.value == 'lambda':
return self.parse_lambda()
raise SyntaxError(f"Unexpected token {tok}")
# ----------------------------------------------------------------------
# Evaluator
# ----------------------------------------------------------------------
class Evaluator:
def __init__(self, global_env):
self.global_env = global_env
def evaluate(self, node, env):
if isinstance(node, Number):
return node.value
if isinstance(node, ImagLiteral):
return FlamePLComplex(0, node.value)
if isinstance(node, String):
return node.value
if isinstance(node, BooleanLiteral): # <-- return Python bool
return node.value
if isinstance(node, NullLiteral):
return None
if isinstance(node, Var):
return env.get(node.name)
if isinstance(node, Assign):
val = self.evaluate(node.expr, env)
env.set(node.name, val)
return val
if isinstance(node, BinaryOp):
left = self.evaluate(node.left, env)
right = self.evaluate(node.right, env)
op = node.op
if op == 'and':
return bool(left and right) # <-- force boolean
if op == 'or':
return bool(left or right) # <-- force boolean
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 left == right # already bool
if op == '<':
return left < right
if op == '>':
return left > right
raise RuntimeError(f"Unknown binary op: {op}")
if isinstance(node, UnaryOp):
val = self.evaluate(node.expr, env)
if node.op == '-':
return -val
if node.op == '+':
return val
if node.op == 'not':
return not bool(val) # <-- force boolean
raise RuntimeError(f"Unknown unary op: {node.op}")
if isinstance(node, If):
cond = self.evaluate(node.cond, env)
if cond:
return self.evaluate_block(node.then_block, env)
else:
return self.evaluate_block(node.else_block, env)
if isinstance(node, While):
while self.evaluate(node.cond, env):
self.evaluate_block(node.body, env)
return None
if isinstance(node, Return):
raise ReturnException(self.evaluate(node.expr, env))
if isinstance(node, FunctionDef):
func = FlamePLFunction(node.params, node.body, env, node.name)
env.set(node.name, func)
return func
if isinstance(node, LambdaDef):
return FlamePLFunction(node.params, node.body, env)
if isinstance(node, ClassDef):
parent = None
if node.parent_name:
parent = env.get(node.parent_name)
if not isinstance(parent, FlamePLClass):
raise TypeError(f"Parent {node.parent_name} is not a class")
methods = {}
for mname, fn_def in node.methods.items():
methods[mname] = FlamePLFunction(fn_def.params, fn_def.body, env, f"{node.name}.{mname}")
klass = FlamePLClass(node.name, methods, parent)
env.set(node.name, klass)
return klass
if isinstance(node, Call):
func = self.evaluate(node.func, env)
args = [self.evaluate(a, env) for a in node.args]
if isinstance(func, FlamePLFunction):
return func.call(args, self)
if isinstance(func, FlamePLClass):
return func.call(args, self)
if callable(func):
return func(*args)
raise TypeError(f"{func} is not callable")
if isinstance(node, MethodCall):
obj = self.evaluate(node.obj, env)
args = [self.evaluate(a, env) for a in node.args]
if isinstance(obj, FlamePLInstance):
method = obj.get_method(node.method)
if method:
return method.call(args, self)
raise TypeError(f"No method {node.method} on {obj}")
if isinstance(node, Index):
obj = self.evaluate(node.obj, env)
idx = self.evaluate(node.index, env)
if isinstance(obj, list):
return obj[idx]
if isinstance(obj, dict):
return obj[idx]
raise TypeError(f"Cannot index {obj}")
if isinstance(node, ListLiteral):
return [self.evaluate(e, env) for e in node.elements]
if isinstance(node, PairLiteral):
return FlamePLPair(self.evaluate(node.first, env), self.evaluate(node.second, env))
if isinstance(node, DictLiteral):
d = {}
for k, v in node.items:
d[self.evaluate(k, env)] = self.evaluate(v, env)
return d
if isinstance(node, Print):
val = self.evaluate(node.expr, env)
print(val)
return None
if isinstance(node, Input):
prompt = self.evaluate(node.prompt, env)
result = input(str(prompt))
return result
if isinstance(node, TypeOf):
val = self.evaluate(node.expr, env)
if isinstance(val, bool): # <-- recognize bool type
return BOOL_TYPE
if isinstance(val, int) or isinstance(val, Decimal):
return INT_TYPE if not isinstance(val, Decimal) or val.as_tuple().exponent >= 0 else FLOAT_TYPE
if isinstance(val, float):
return FLOAT_TYPE
if isinstance(val, FlamePLComplex):
return COMPLEX_TYPE
if isinstance(val, list):
return LIST_TYPE
if isinstance(val, FlamePLPair):
return PAIR_TYPE
if isinstance(val, dict):
return DICT_TYPE
if isinstance(val, str):
return STR_TYPE
if val is None:
return NULL_TYPE
if isinstance(val, (FlamePLFunction, FlamePLBoundMethod)):
return FUNCTION_TYPE
if isinstance(val, (FlamePLClass, FlamePLInstance)):
return CLASS_TYPE
return FlamePLType(type(val).__name__)
if isinstance(node, Convert):
val = self.evaluate(node.expr, env)
if node.kind == 'int':
return int(val)
if node.kind == 'float':
return Decimal(str(val))
if node.kind == 'str':
return str(val)
raise RuntimeError(f"Unknown conversion: {node.kind}")
raise RuntimeError(f"Unknown AST node: {type(node)}")
def evaluate_block(self, block, env):
result = None
for stmt in block:
result = self.evaluate(stmt, env)
return result
# ----------------------------------------------------------------------
# Main entry
# ----------------------------------------------------------------------
def run_flame(source):
lexer = Lexer(source)
tokens = lexer.tokenize()
parser = Parser(tokens)
ast = parser.parse()
global_env = Environment()
# built‑in functions
def print_fn(*args):
print(' '.join(str(a) for a in args))
def input_fn(prompt):
return input(str(prompt))
global_env.define('print', print_fn)
global_env.define('input', input_fn)
evaluator = Evaluator(global_env)
evaluator.evaluate_block(ast, global_env)
# ----------------------------------------------------------------------
# Updated sample program with booleans
# ----------------------------------------------------------------------
SAMPLE_PROGRAM = """
# 1. Arbitrary precision float
a <- 1.23456789012345678901234567890
b <- 2.0
c <- a * b
print("Arbitrary float result: " + to_str(c))
# 2. Lists, ordered pair, complex, dict
my_list <- [1, 2, 3, 4]
my_pair <- (10, 20)
my_complex <- 3 + 4i
my_dict <- {"name": "FlamePL", "version": 1.0}
print(my_list)
print(my_pair)
print(my_complex)
print(my_dict)
# 3. Input and output (commented out to run non‑interactively)
# name <- input("Enter your name: ")
# print("Hello, " + name)
# 4. Multi-line string
multiline <- """
This is line 1
This is line 2
This is line 3
"""
print(multiline)
# 5. OOP
class Animal
fn init(name)
self.name <- name
end
fn speak()
print(self.name + " makes a noise.")
end
end
class Dog : Animal
fn speak()
print(self.name + " barks!")
end
end
d <- Dog("Rex")
d.speak()
# 6. Function, lambda, closures, recursion
fn factorial(n)
if n = 0 then
return 1
else
return n * factorial(n - 1)
end
end
print("Factorial of 5: " + to_str(factorial(5)))
# Lambda
square <- lambda (x) -> x * x
print("Square of 6: " + to_str(square(6)))
# Closure
fn make_counter()
count <- 0
fn inner()
count <- count + 1
return count
end
return inner
end
counter <- make_counter()
print(counter()) # 1
print(counter()) # 2
# 7. Type convert
num_str <- "3.14159"
pi <- to_float(num_str)
print(pi)
# 8. Treat type itself as data type
t <- type_of(10)
if t = int then
print("10 is an integer")
end
# 9. Null value
n <- null
print(n) # prints null
# 10. Exponent ^, integer division //, comment #
res1 <- 2 ^ 10 # 1024
res2 <- 10 // 3 # 3
print("2^10 = " + to_str(res1))
print("10 // 3 = " + to_str(res2))
# 11. Logical operators in English (now return booleans)
if true and false then
print("This won't print")
else
print("Logical 'and' works")
end
if true or false then
print("Logical 'or' works")
end
if not false then
print("Logical 'not' works")
end
# 12. Assignment <-, equality =
x <- 5
if x = 5 then
print("Assignment and equality check passed")
end
# ---- NEW: Demonstrate boolean literals and type ----
b1 <- true
b2 <- false
print("b1 = " + to_str(b1))
print("b2 = " + to_str(b2))
print("type_of(b1) = " + to_str(type_of(b1)))
print("type_of(b1) = bool? " + to_str(type_of(b1) = bool))
# Logical operators always return booleans
result_and <- true and false
result_or <- true or false
result_not <- not true
print("true and false = " + to_str(result_and))
print("true or false = " + to_str(result_or))
print("not true = " + to_str(result_not))
print("type_of(result_and) = " + to_str(type_of(result_and)))
"""
if __name__ == "__main__":
# To read from a file, uncomment:
# with open("program.flpl", "r") as f:
# run_flame(f.read())
run_flame(SAMPLE_PROGRAM)