YREA-SLS/SLS_Python/sls_py/builtin.py

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