1268 lines
35 KiB
Python
1268 lines
35 KiB
Python
from __future__ import annotations
|
|
import math
|
|
import random
|
|
from typing import TYPE_CHECKING, Optional, Tuple
|
|
|
|
if TYPE_CHECKING:
|
|
from .interpreter import InterpreterState, StackEntry
|
|
|
|
from .interpreter_types import StackType
|
|
from .lexer import Token, TokenType, IntegerLiteral, IntegerBuiltInType, Identifier
|
|
|
|
|
|
# =====================================================================
|
|
# Helper Functions
|
|
# =====================================================================
|
|
|
|
class NumericTypeFlags:
|
|
"""Flags to track numeric type properties"""
|
|
FLOAT = 1 << 0
|
|
SIGNED = 1 << 1
|
|
BITS_64 = 1 << 2
|
|
BITS_32 = 1 << 3
|
|
BITS_16 = 1 << 4
|
|
BITS_8 = 1 << 5
|
|
|
|
|
|
def extract_numeric_value(entry: "StackEntry") -> Optional[Tuple[float, int, int]]:
|
|
"""
|
|
Extract numeric value from stack entry.
|
|
Returns: (float_value, int_value, type_flags) or None if not numeric
|
|
"""
|
|
flags = 0
|
|
fval = 0.0
|
|
ival = 0
|
|
|
|
if entry.type == StackType.I64:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_64 | NumericTypeFlags.SIGNED
|
|
elif entry.type == StackType.I32:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_32 | NumericTypeFlags.SIGNED
|
|
elif entry.type == StackType.I16:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_16 | NumericTypeFlags.SIGNED
|
|
elif entry.type == StackType.I8:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_8 | NumericTypeFlags.SIGNED
|
|
elif entry.type == StackType.U64:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_64
|
|
elif entry.type == StackType.U32:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_32
|
|
elif entry.type == StackType.U16:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_16
|
|
elif entry.type == StackType.U8:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_8
|
|
elif entry.type == StackType.FLOAT:
|
|
fval = float(entry.value) # type: ignore
|
|
flags = NumericTypeFlags.BITS_32 | NumericTypeFlags.FLOAT
|
|
elif entry.type == StackType.DOUBLE:
|
|
fval = float(entry.value) # type: ignore
|
|
flags = NumericTypeFlags.BITS_64 | NumericTypeFlags.FLOAT
|
|
elif entry.type == StackType.CHARACTER:
|
|
ival = int(entry.value) # type: ignore
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_8
|
|
elif entry.type == StackType.BOOLEAN:
|
|
ival = 1 if entry.value else 0
|
|
fval = float(ival)
|
|
flags = NumericTypeFlags.BITS_8
|
|
else:
|
|
return None
|
|
|
|
return (fval, ival, flags)
|
|
|
|
|
|
def get_numeric_two(state: "InterpreterState") -> Optional[Tuple[float, float, int, int, int]]:
|
|
"""
|
|
Extract two numeric values from top of stack.
|
|
Returns: (fval_a, fval_b, ival_a, ival_b, combined_flags) or None
|
|
"""
|
|
if len(state.stack) < 2:
|
|
return None
|
|
|
|
entry_a = state.stack[-1]
|
|
entry_b = state.stack[-2]
|
|
|
|
result_a = extract_numeric_value(entry_a)
|
|
result_b = extract_numeric_value(entry_b)
|
|
|
|
if result_a is None or result_b is None:
|
|
return None
|
|
|
|
fval_a, ival_a, flags_a = result_a
|
|
fval_b, ival_b, flags_b = result_b
|
|
|
|
# Combine flags (bitwise OR)
|
|
combined_flags = flags_a | flags_b
|
|
|
|
return (fval_a, fval_b, ival_a, ival_b, combined_flags)
|
|
|
|
|
|
def get_numeric_one(state: "InterpreterState") -> Optional[Tuple[float, int, int]]:
|
|
"""
|
|
Extract one numeric value from top of stack.
|
|
Returns: (fval, ival, flags) or None
|
|
"""
|
|
if len(state.stack) < 1:
|
|
return None
|
|
|
|
entry = state.stack[-1]
|
|
result = extract_numeric_value(entry)
|
|
|
|
if result is None:
|
|
return None
|
|
|
|
return result
|
|
|
|
|
|
def create_numeric_token(flags: int, fval: float, ival: int) -> Token:
|
|
"""Create a token from numeric value based on type flags"""
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
if flags & NumericTypeFlags.BITS_64:
|
|
return Token(type=TokenType.DOUBLE, double_literal=fval)
|
|
else:
|
|
return Token(type=TokenType.FLOAT, float_literal=float(fval))
|
|
else:
|
|
# Integer type
|
|
if flags & NumericTypeFlags.SIGNED:
|
|
if flags & NumericTypeFlags.BITS_64:
|
|
int_type = IntegerBuiltInType.I64
|
|
elif flags & NumericTypeFlags.BITS_32:
|
|
int_type = IntegerBuiltInType.I32
|
|
elif flags & NumericTypeFlags.BITS_16:
|
|
int_type = IntegerBuiltInType.I16
|
|
else:
|
|
int_type = IntegerBuiltInType.I8
|
|
else:
|
|
if flags & NumericTypeFlags.BITS_64:
|
|
int_type = IntegerBuiltInType.U64
|
|
elif flags & NumericTypeFlags.BITS_32:
|
|
int_type = IntegerBuiltInType.U32
|
|
elif flags & NumericTypeFlags.BITS_16:
|
|
int_type = IntegerBuiltInType.U16
|
|
else:
|
|
int_type = IntegerBuiltInType.U8
|
|
|
|
return Token(
|
|
type=TokenType.INTEGER,
|
|
integer_literal=IntegerLiteral(value=ival, type=int_type)
|
|
)
|
|
|
|
|
|
def is_truthy(entry: "StackEntry") -> bool:
|
|
"""Check if a stack entry is truthy"""
|
|
if entry.type == StackType.IDENTIFIER:
|
|
return False
|
|
elif entry.type in (StackType.I64, StackType.I32, StackType.I16, StackType.I8,
|
|
StackType.U64, StackType.U32, StackType.U16, StackType.U8):
|
|
return bool(entry.value)
|
|
elif entry.type in (StackType.FLOAT, StackType.DOUBLE):
|
|
return bool(entry.value)
|
|
elif entry.type == StackType.CHARACTER:
|
|
return bool(entry.value)
|
|
elif entry.type == StackType.BOOLEAN:
|
|
return bool(entry.value)
|
|
elif entry.type in (StackType.TOKEN_STRING, StackType.CALLABLE):
|
|
ts = entry.value
|
|
return len(ts.tokens) > 0 and ts.tokens[0].type != TokenType.EOF # type: ignore
|
|
return False
|
|
|
|
|
|
def get_scalar_value(entry: "StackEntry") -> Optional[int]:
|
|
"""Extract a scalar (non-negative integer) value from stack entry"""
|
|
if entry.type == StackType.I64:
|
|
val = int(entry.value) # type: ignore
|
|
if val < 0:
|
|
return None
|
|
return val
|
|
elif entry.type == StackType.I32:
|
|
val = int(entry.value) # type: ignore
|
|
if val < 0:
|
|
return None
|
|
return val
|
|
elif entry.type == StackType.I16:
|
|
val = int(entry.value) # type: ignore
|
|
if val < 0:
|
|
return None
|
|
return val
|
|
elif entry.type == StackType.I8:
|
|
val = int(entry.value) # type: ignore
|
|
if val < 0:
|
|
return None
|
|
return val
|
|
elif entry.type in (StackType.U64, StackType.U32, StackType.U16, StackType.U8):
|
|
return int(entry.value) # type: ignore
|
|
elif entry.type == StackType.CHARACTER:
|
|
return int(entry.value) # type: ignore
|
|
elif entry.type == StackType.BOOLEAN:
|
|
return 1 if entry.value else 0
|
|
else:
|
|
return None
|
|
|
|
|
|
# =====================================================================
|
|
# Arithmetic Operations
|
|
# =====================================================================
|
|
|
|
def builtin_addition(state: "InterpreterState") -> bool:
|
|
"""Add two numeric values"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, ival_a, ival_b, flags = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = fval_b + fval_a
|
|
token = create_numeric_token(flags, result_val, 0)
|
|
else:
|
|
result_val = ival_b + ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_subtraction(state: "InterpreterState") -> bool:
|
|
"""Subtract two numeric values"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, ival_a, ival_b, flags = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = fval_b - fval_a
|
|
token = create_numeric_token(flags, result_val, 0)
|
|
else:
|
|
result_val = ival_b - ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_multiplication(state: "InterpreterState") -> bool:
|
|
"""Multiply two numeric values"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, ival_a, ival_b, flags = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = fval_b * fval_a
|
|
token = create_numeric_token(flags, result_val, 0)
|
|
else:
|
|
result_val = ival_b * ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_division(state: "InterpreterState") -> bool:
|
|
"""Divide two numeric values (always returns float)"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
|
|
if fval_a == 0:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = fval_b / fval_a
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=result_val))
|
|
|
|
|
|
def builtin_modulus(state: "InterpreterState") -> bool:
|
|
"""Modulo operation (integers only)"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
if ival_a == 0:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b % ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_exponential(state: "InterpreterState") -> bool:
|
|
"""Exponentiation (always returns float)"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = math.pow(fval_b, fval_a)
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=result_val))
|
|
|
|
|
|
# =====================================================================
|
|
# Comparison Operations
|
|
# =====================================================================
|
|
|
|
def builtin_greater_than(state: "InterpreterState") -> bool:
|
|
"""Greater than comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b > fval_a))
|
|
|
|
|
|
def builtin_greater_than_or_equal_to(state: "InterpreterState") -> bool:
|
|
"""Greater than or equal comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b >= fval_a))
|
|
|
|
|
|
def builtin_less_than(state: "InterpreterState") -> bool:
|
|
"""Less than comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b < fval_a))
|
|
|
|
|
|
def builtin_less_than_or_equal_to(state: "InterpreterState") -> bool:
|
|
"""Less than or equal comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b <= fval_a))
|
|
|
|
|
|
def builtin_equal_to(state: "InterpreterState") -> bool:
|
|
"""Equality comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b == fval_a))
|
|
|
|
|
|
def builtin_not_equal_to(state: "InterpreterState") -> bool:
|
|
"""Inequality comparison"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=fval_b != fval_a))
|
|
|
|
|
|
# =====================================================================
|
|
# Mathematical Functions
|
|
# =====================================================================
|
|
|
|
def builtin_abs(state: "InterpreterState") -> bool:
|
|
"""Absolute value"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, ival, flags = result
|
|
|
|
# Only works on signed types or floats
|
|
if not ((flags & NumericTypeFlags.SIGNED) or (flags & NumericTypeFlags.FLOAT)):
|
|
return False
|
|
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = abs(fval)
|
|
token = create_numeric_token(flags, result_val, 0)
|
|
else:
|
|
result_val = abs(ival)
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_sqrt(state: "InterpreterState") -> bool:
|
|
"""Square root"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
|
|
if fval < 0:
|
|
return False
|
|
|
|
state.pop()
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.sqrt(fval)))
|
|
|
|
|
|
def builtin_sin(state: "InterpreterState") -> bool:
|
|
"""Sine function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.sin(fval)))
|
|
|
|
|
|
def builtin_cos(state: "InterpreterState") -> bool:
|
|
"""Cosine function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.cos(fval)))
|
|
|
|
|
|
def builtin_tan(state: "InterpreterState") -> bool:
|
|
"""Tangent function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.tan(fval)))
|
|
|
|
|
|
def builtin_asin(state: "InterpreterState") -> bool:
|
|
"""Arc sine function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
|
|
if fval < -1 or fval > 1:
|
|
return False
|
|
|
|
state.pop()
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.asin(fval)))
|
|
|
|
|
|
def builtin_acos(state: "InterpreterState") -> bool:
|
|
"""Arc cosine function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
|
|
if fval < -1 or fval > 1:
|
|
return False
|
|
|
|
state.pop()
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.acos(fval)))
|
|
|
|
|
|
def builtin_atan(state: "InterpreterState") -> bool:
|
|
"""Arc tangent function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.atan(fval)))
|
|
|
|
|
|
def builtin_atan2(state: "InterpreterState") -> bool:
|
|
"""Two-argument arc tangent function"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, _, _, _ = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.atan2(fval_b, fval_a)))
|
|
|
|
|
|
def builtin_floor(state: "InterpreterState") -> bool:
|
|
"""Floor function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.floor(fval)))
|
|
|
|
|
|
def builtin_ceil(state: "InterpreterState") -> bool:
|
|
"""Ceiling function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.ceil(fval)))
|
|
|
|
|
|
def builtin_round(state: "InterpreterState") -> bool:
|
|
"""Round function"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
state.pop()
|
|
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=round(fval)))
|
|
|
|
|
|
def builtin_ln(state: "InterpreterState") -> bool:
|
|
"""Natural logarithm"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
|
|
if fval <= 0:
|
|
return False
|
|
|
|
state.pop()
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.log(fval)))
|
|
|
|
|
|
def builtin_log(state: "InterpreterState") -> bool:
|
|
"""Base-10 logarithm"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, _, _ = result
|
|
|
|
if fval <= 0:
|
|
return False
|
|
|
|
state.pop()
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=math.log10(fval)))
|
|
|
|
|
|
# =====================================================================
|
|
# Logical Operations
|
|
# =====================================================================
|
|
|
|
def builtin_and(state: "InterpreterState") -> bool:
|
|
"""Logical AND"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, ival_a, ival_b, flags = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = bool(fval_b and fval_a)
|
|
else:
|
|
result_val = bool(ival_b and ival_a)
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=result_val))
|
|
|
|
|
|
def builtin_or(state: "InterpreterState") -> bool:
|
|
"""Logical OR"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval_a, fval_b, ival_a, ival_b, flags = result
|
|
state.pop()
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = bool(fval_b or fval_a)
|
|
else:
|
|
result_val = bool(ival_b or ival_a)
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=result_val))
|
|
|
|
|
|
def builtin_not(state: "InterpreterState") -> bool:
|
|
"""Logical NOT"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
fval, ival, flags = result
|
|
state.pop()
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
result_val = not bool(fval)
|
|
else:
|
|
result_val = not bool(ival)
|
|
|
|
return state.push_token(Token(type=TokenType.BOOLEAN, boolean_literal=result_val))
|
|
|
|
|
|
# =====================================================================
|
|
# Bitwise Operations
|
|
# =====================================================================
|
|
|
|
def builtin_bitand(state: "InterpreterState") -> bool:
|
|
"""Bitwise AND"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b & ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_bitor(state: "InterpreterState") -> bool:
|
|
"""Bitwise OR"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b | ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_bitxor(state: "InterpreterState") -> bool:
|
|
"""Bitwise XOR"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b ^ ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_bitnot(state: "InterpreterState") -> bool:
|
|
"""Bitwise NOT"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, ival, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
|
|
result_val = ~ival
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_shl(state: "InterpreterState") -> bool:
|
|
"""Bitwise left shift"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b << ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
def builtin_shr(state: "InterpreterState") -> bool:
|
|
"""Bitwise right shift"""
|
|
result = get_numeric_two(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, _, ival_a, ival_b, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
result_val = ival_b >> ival_a
|
|
token = create_numeric_token(flags, 0.0, result_val)
|
|
return state.push_token(token)
|
|
|
|
|
|
# =====================================================================
|
|
# Stack Operations
|
|
# =====================================================================
|
|
|
|
def builtin_dup(state: "InterpreterState") -> bool:
|
|
"""Duplicate top stack item"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
top = state.stack[-1]
|
|
|
|
# Deep copy for token strings
|
|
if top.type in (StackType.TOKEN_STRING, StackType.CALLABLE):
|
|
value_copy = top.value.deep_copy() # type: ignore
|
|
else:
|
|
value_copy = top.value
|
|
|
|
from .interpreter import StackEntry
|
|
state.push(StackEntry(type=top.type, value=value_copy))
|
|
return True
|
|
|
|
|
|
def builtin_drop(state: "InterpreterState") -> bool:
|
|
"""Drop top stack item"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
state.pop()
|
|
return True
|
|
|
|
|
|
def builtin_swap(state: "InterpreterState") -> bool:
|
|
"""Swap top two stack items"""
|
|
if len(state.stack) < 2:
|
|
return False
|
|
|
|
a = state.pop()
|
|
b = state.pop()
|
|
state.push(a) # type: ignore
|
|
state.push(b) # type: ignore
|
|
return True
|
|
|
|
|
|
def builtin_depth(state: "InterpreterState") -> bool:
|
|
"""Push stack depth"""
|
|
depth = len(state.stack)
|
|
return state.push_token(Token(
|
|
type=TokenType.INTEGER,
|
|
integer_literal=IntegerLiteral(value=depth, type=IntegerBuiltInType.U64)
|
|
))
|
|
|
|
|
|
def builtin_pick(state: "InterpreterState") -> bool:
|
|
"""Copy nth item to top (0 = top)"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, ival, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
n = int(ival)
|
|
|
|
if n < 0 or n >= len(state.stack) - 1:
|
|
return False
|
|
|
|
state.pop() # Remove the index
|
|
|
|
# Pick from stack (0 = current top, 1 = one below, etc.)
|
|
picked_idx = len(state.stack) - 1 - n
|
|
if picked_idx < 0:
|
|
return False
|
|
|
|
picked = state.stack[picked_idx]
|
|
|
|
# Deep copy for token strings
|
|
if picked.type in (StackType.TOKEN_STRING, StackType.CALLABLE):
|
|
value_copy = picked.value.deep_copy() # type: ignore
|
|
else:
|
|
value_copy = picked.value
|
|
|
|
from .interpreter import StackEntry
|
|
state.push(StackEntry(type=picked.type, value=value_copy))
|
|
return True
|
|
|
|
|
|
def builtin_roll(state: "InterpreterState") -> bool:
|
|
"""Rotate top n items, times times"""
|
|
if len(state.stack) < 2:
|
|
return False
|
|
|
|
times_entry = state.stack[-1]
|
|
count_entry = state.stack[-2]
|
|
|
|
times_val = get_scalar_value(times_entry)
|
|
count_val = get_scalar_value(count_entry)
|
|
|
|
if times_val is None or count_val is None:
|
|
return False
|
|
|
|
if count_val <= 0:
|
|
state.pop()
|
|
state.pop()
|
|
return True
|
|
|
|
if count_val > len(state.stack) - 2:
|
|
return False
|
|
|
|
state.pop()
|
|
state.pop()
|
|
|
|
# Extract the top count_val items
|
|
items = []
|
|
for _ in range(count_val):
|
|
if len(state.stack) < 1:
|
|
return False
|
|
items.append(state.pop())
|
|
|
|
items.reverse() # Reverse to get proper order
|
|
|
|
# Rotate items by times_val
|
|
times_val = times_val % len(items) if len(items) > 0 else 0
|
|
rotated = items[times_val:] + items[:times_val]
|
|
|
|
# Push back in reverse order
|
|
for item in reversed(rotated):
|
|
state.push(item)
|
|
|
|
return True
|
|
|
|
|
|
# =====================================================================
|
|
# Control Flow Operations
|
|
# =====================================================================
|
|
|
|
def builtin_if(state: "InterpreterState") -> bool:
|
|
"""Conditional execution: condition then_block else_block if"""
|
|
if len(state.stack) < 3:
|
|
return False
|
|
|
|
else_block = state.pop()
|
|
then_block = state.pop()
|
|
condition = state.pop()
|
|
|
|
# Check that blocks are executable
|
|
if else_block.type not in (StackType.TOKEN_STRING, StackType.CALLABLE, StackType.IDENTIFIER): # type: ignore
|
|
return False
|
|
if then_block.type not in (StackType.TOKEN_STRING, StackType.CALLABLE, StackType.IDENTIFIER): # type: ignore
|
|
return False
|
|
|
|
# Evaluate condition
|
|
is_true = is_truthy(condition) # type: ignore
|
|
|
|
# Execute appropriate block
|
|
if is_true:
|
|
block_to_execute = then_block
|
|
else:
|
|
block_to_execute = else_block
|
|
|
|
if block_to_execute.type == StackType.IDENTIFIER: # type: ignore
|
|
return state.execute_func(block_to_execute.value.name) # type: ignore
|
|
else:
|
|
return state.execute_token_string(block_to_execute.value) # type: ignore
|
|
|
|
|
|
def builtin_while(state: "InterpreterState") -> bool:
|
|
"""While loop: condition_block body_block while"""
|
|
if len(state.stack) < 2:
|
|
return False
|
|
|
|
body_block = state.pop()
|
|
condition_block = state.pop()
|
|
|
|
# Check that blocks are executable
|
|
if body_block.type not in (StackType.TOKEN_STRING, StackType.CALLABLE, StackType.IDENTIFIER): # type: ignore
|
|
return False
|
|
if condition_block.type not in (StackType.TOKEN_STRING, StackType.CALLABLE, StackType.IDENTIFIER): # type: ignore
|
|
return False
|
|
|
|
while True:
|
|
# Execute condition block
|
|
if condition_block.type == StackType.IDENTIFIER: # type: ignore
|
|
if not state.execute_func(condition_block.value.name): # type: ignore
|
|
return False
|
|
else:
|
|
if not state.execute_token_string(condition_block.value): # type: ignore
|
|
return False
|
|
|
|
# Check result
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
condition_result = state.pop()
|
|
if not is_truthy(condition_result): # type: ignore
|
|
break
|
|
|
|
# Execute body block
|
|
if body_block.type == StackType.IDENTIFIER: # type: ignore
|
|
if not state.execute_func(body_block.value.name): # type: ignore
|
|
return False
|
|
else:
|
|
if not state.execute_token_string(body_block.value): # type: ignore
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def builtin_for(state: "InterpreterState") -> bool:
|
|
"""For loop: start end body_block for"""
|
|
if len(state.stack) < 3:
|
|
return False
|
|
|
|
body_block = state.pop()
|
|
end_item = state.pop()
|
|
start_item = state.pop()
|
|
|
|
# Check that block is executable
|
|
if body_block.type not in (StackType.TOKEN_STRING, StackType.CALLABLE, StackType.IDENTIFIER): # type: ignore
|
|
return False
|
|
|
|
start_val = get_scalar_value(start_item) # type: ignore
|
|
end_val = get_scalar_value(end_item) # type: ignore
|
|
|
|
if start_val is None or end_val is None:
|
|
return False
|
|
|
|
# Execute loop
|
|
for i in range(start_val, end_val):
|
|
# Push counter
|
|
if not state.push_token(Token(
|
|
type=TokenType.INTEGER,
|
|
integer_literal=IntegerLiteral(value=i, type=IntegerBuiltInType.I64)
|
|
)):
|
|
return False
|
|
|
|
# Execute body
|
|
if body_block.type == StackType.IDENTIFIER: # type: ignore
|
|
if not state.execute_func(body_block.value.name): # type: ignore
|
|
return False
|
|
else:
|
|
if not state.execute_token_string(body_block.value): # type: ignore
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
# =====================================================================
|
|
# Special Operations
|
|
# =====================================================================
|
|
|
|
def builtin_eval(state: "InterpreterState") -> bool:
|
|
"""Evaluate code block or identifier"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
code_target = state.pop()
|
|
|
|
if code_target.type in (StackType.TOKEN_STRING, StackType.CALLABLE): # type: ignore
|
|
return state.execute_token_string(code_target.value) # type: ignore
|
|
elif code_target.type == StackType.IDENTIFIER: # type: ignore
|
|
return state.execute_func(code_target.value.name) # type: ignore
|
|
else:
|
|
return False
|
|
|
|
|
|
def builtin_lambda(state: "InterpreterState") -> bool:
|
|
"""Convert token string to callable"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
if state.stack[-1].type != StackType.TOKEN_STRING:
|
|
return False
|
|
|
|
state.stack[-1].type = StackType.CALLABLE
|
|
return True
|
|
|
|
|
|
def builtin_const(state: "InterpreterState") -> bool:
|
|
"""Define a constant: value ::name const"""
|
|
if len(state.stack) < 2:
|
|
return False
|
|
|
|
name_entry = state.pop()
|
|
value_entry = state.pop()
|
|
|
|
if name_entry.type != StackType.IDENTIFIER: # type: ignore
|
|
return False
|
|
|
|
if value_entry.type != StackType.CALLABLE: # type: ignore
|
|
return False
|
|
|
|
name = name_entry.value.name # type: ignore
|
|
|
|
# Check if function already exists
|
|
if state.get_function(name) is not None:
|
|
return False
|
|
|
|
# Create function item
|
|
state.add_function(name, value_entry.value) # type: ignore
|
|
|
|
return True
|
|
|
|
|
|
def builtin_type_of(state: "InterpreterState") -> bool:
|
|
"""Get the type of the top stack item"""
|
|
if len(state.stack) < 1:
|
|
return False
|
|
|
|
item = state.pop()
|
|
|
|
# Map stack type to type name
|
|
type_names = {
|
|
StackType.IDENTIFIER: "Identifier",
|
|
StackType.I64: "i64",
|
|
StackType.I32: "i32",
|
|
StackType.I16: "i16",
|
|
StackType.I8: "i8",
|
|
StackType.U64: "u64",
|
|
StackType.U32: "u32",
|
|
StackType.U16: "u16",
|
|
StackType.U8: "u8",
|
|
StackType.FLOAT: "f32",
|
|
StackType.DOUBLE: "f64",
|
|
StackType.CHARACTER: "char",
|
|
StackType.BOOLEAN: "bool",
|
|
StackType.TOKEN_STRING: "TokenString",
|
|
StackType.CALLABLE: "Callable",
|
|
}
|
|
|
|
type_name = type_names.get(item.type, "Unknown") # type: ignore
|
|
|
|
return state.push_token(Token(
|
|
type=TokenType.IDENTIFIER,
|
|
identifier=Identifier(name=type_name, is_literal=True)
|
|
))
|
|
|
|
|
|
# =====================================================================
|
|
# Random Number Operations
|
|
# =====================================================================
|
|
|
|
def builtin_rand(state: "InterpreterState") -> bool:
|
|
"""Generate random number [0.0, 1.0)"""
|
|
return state.push_token(Token(type=TokenType.DOUBLE, double_literal=random.random()))
|
|
|
|
|
|
def builtin_seed(state: "InterpreterState") -> bool:
|
|
"""Seed random number generator"""
|
|
result = get_numeric_one(state)
|
|
if result is None:
|
|
return False
|
|
|
|
_, ival, flags = result
|
|
|
|
if flags & NumericTypeFlags.FLOAT:
|
|
return False
|
|
|
|
state.pop()
|
|
random.seed(ival)
|
|
return True
|
|
|
|
|
|
# =====================================================================
|
|
# Load Builtins
|
|
# =====================================================================
|
|
|
|
def load_builtins(interpreter_state: "InterpreterState") -> bool:
|
|
"""Load all builtin functions into the interpreter state"""
|
|
|
|
builtins = {
|
|
# Arithmetic
|
|
"+": builtin_addition,
|
|
"-": builtin_subtraction,
|
|
"*": builtin_multiplication,
|
|
"/": builtin_division,
|
|
"%": builtin_modulus,
|
|
"^": builtin_exponential,
|
|
|
|
# Comparison
|
|
">": builtin_greater_than,
|
|
">=": builtin_greater_than_or_equal_to,
|
|
"<": builtin_less_than,
|
|
"<=": builtin_less_than_or_equal_to,
|
|
"==": builtin_equal_to,
|
|
"!=": builtin_not_equal_to,
|
|
|
|
# Mathematical functions
|
|
"abs": builtin_abs,
|
|
"sqrt": builtin_sqrt,
|
|
"sin": builtin_sin,
|
|
"cos": builtin_cos,
|
|
"tan": builtin_tan,
|
|
"asin": builtin_asin,
|
|
"acos": builtin_acos,
|
|
"atan": builtin_atan,
|
|
"atan2": builtin_atan2,
|
|
"floor": builtin_floor,
|
|
"ceil": builtin_ceil,
|
|
"round": builtin_round,
|
|
"ln": builtin_ln,
|
|
"log": builtin_log,
|
|
|
|
# Logical
|
|
"and": builtin_and,
|
|
"or": builtin_or,
|
|
"not": builtin_not,
|
|
|
|
# Bitwise
|
|
"bitand": builtin_bitand,
|
|
"bitor": builtin_bitor,
|
|
"bitxor": builtin_bitxor,
|
|
"bitnot": builtin_bitnot,
|
|
"shl": builtin_shl,
|
|
"shr": builtin_shr,
|
|
|
|
# Stack operations
|
|
"dup": builtin_dup,
|
|
"drop": builtin_drop,
|
|
"swap": builtin_swap,
|
|
"depth": builtin_depth,
|
|
"pick": builtin_pick,
|
|
"roll": builtin_roll,
|
|
|
|
# Control flow
|
|
"if": builtin_if,
|
|
"while": builtin_while,
|
|
"for": builtin_for,
|
|
|
|
# Special
|
|
"eval": builtin_eval,
|
|
"lambda": builtin_lambda,
|
|
"const": builtin_const,
|
|
"type_of": builtin_type_of,
|
|
|
|
# Random
|
|
"rand": builtin_rand,
|
|
"seed": builtin_seed,
|
|
}
|
|
|
|
# Register all builtins
|
|
for name, func in builtins.items():
|
|
interpreter_state.add_function(name, func)
|
|
|
|
# Load SLS-defined builtins
|
|
try:
|
|
from .lexer import LexerInfo, lexical_analysis
|
|
|
|
builtin_code = """
|
|
// Kyler Olsen
|
|
// YREA SLS
|
|
// SLS Defined Builtin Operators
|
|
// November 2025
|
|
|
|
{ ln swap ln swap / } lambda ::logb const
|
|
{ 1 pick 1 pick < { swap drop } { drop } if } lambda ::max const
|
|
{ 1 pick 1 pick < { drop } { swap drop } if } lambda ::min const
|
|
{ 3 1 roll } lambda ::rot const
|
|
"""
|
|
|
|
lexer_info = LexerInfo("__builtin.sls", builtin_code)
|
|
tokens = lexical_analysis(lexer_info)
|
|
|
|
for token in tokens:
|
|
if token.type == TokenType.EOF:
|
|
break
|
|
if not interpreter_state.execute(token):
|
|
return False
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Failed to load builtin definitions: {e}")
|
|
return False
|