1187 lines
34 KiB
Rust
1187 lines
34 KiB
Rust
use crate::interpreter::{
|
|
InterpreterState,
|
|
StackValue,
|
|
FunctionItem,
|
|
BuiltinFunction,
|
|
BuiltinFn,
|
|
};
|
|
use crate::lexer::{Token, Identifier};
|
|
use std::collections::HashMap;
|
|
|
|
// Numeric type flags
|
|
bitflags::bitflags! {
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct NumericType: u32 {
|
|
const FLOAT = 1 << 0;
|
|
const SIGNED = 1 << 1;
|
|
const BITS_64 = 1 << 2;
|
|
const BITS_32 = 1 << 3;
|
|
const BITS_16 = 1 << 4;
|
|
const BITS_8 = 1 << 5;
|
|
}
|
|
}
|
|
|
|
struct NumericValue {
|
|
float_val: f64,
|
|
int_val: u64,
|
|
type_flags: NumericType,
|
|
}
|
|
|
|
// Extract numeric value from a single stack item
|
|
fn extract_numeric_type(item: &StackValue) -> Option<NumericValue> {
|
|
let mut val = NumericValue {
|
|
float_val: 0.0,
|
|
int_val: 0,
|
|
type_flags: NumericType::empty(),
|
|
};
|
|
|
|
match item {
|
|
StackValue::I64(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_64 | NumericType::SIGNED;
|
|
}
|
|
StackValue::I32(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_32 | NumericType::SIGNED;
|
|
}
|
|
StackValue::I16(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_16 | NumericType::SIGNED;
|
|
}
|
|
StackValue::I8(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_8 | NumericType::SIGNED;
|
|
}
|
|
StackValue::U64(v) => {
|
|
val.int_val = *v;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_64;
|
|
}
|
|
StackValue::U32(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_32;
|
|
}
|
|
StackValue::U16(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_16;
|
|
}
|
|
StackValue::U8(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_8;
|
|
}
|
|
StackValue::F32(v) => {
|
|
val.float_val = *v as f64;
|
|
val.type_flags = NumericType::BITS_32 | NumericType::FLOAT;
|
|
}
|
|
StackValue::F64(v) => {
|
|
val.float_val = *v;
|
|
val.type_flags = NumericType::BITS_64 | NumericType::FLOAT;
|
|
}
|
|
StackValue::Character(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = *v as f64;
|
|
}
|
|
StackValue::Boolean(v) => {
|
|
val.int_val = *v as u64;
|
|
val.float_val = if *v { 1.0 } else { 0.0 };
|
|
}
|
|
_ => return None,
|
|
}
|
|
|
|
Some(val)
|
|
}
|
|
|
|
// Extract two numeric values from stack
|
|
fn extract_numeric_types(state: &InterpreterState) -> Option<(NumericValue, NumericValue, NumericType)> {
|
|
if state.stack.len() < 2 {
|
|
return None;
|
|
}
|
|
|
|
let a = extract_numeric_type(&state.stack[state.stack.len()-1])?;
|
|
let b = extract_numeric_type(&state.stack[state.stack.len()-2])?;
|
|
|
|
// Combine type flags
|
|
let combined = a.type_flags | b.type_flags;
|
|
|
|
Some((a, b, combined))
|
|
}
|
|
|
|
// Convert numeric result to appropriate token based on type flags
|
|
fn numeric_to_token(float_val: f64, int_val: u64, type_flags: NumericType) -> Token {
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
if type_flags.contains(NumericType::BITS_64) {
|
|
Token::Double(float_val)
|
|
} else {
|
|
Token::Float(float_val as f32)
|
|
}
|
|
} else if type_flags.contains(NumericType::SIGNED) {
|
|
if type_flags.contains(NumericType::BITS_64) {
|
|
Token::I64(int_val as i64)
|
|
} else if type_flags.contains(NumericType::BITS_32) {
|
|
Token::I32(int_val as i32)
|
|
} else if type_flags.contains(NumericType::BITS_16) {
|
|
Token::I16(int_val as i16)
|
|
} else {
|
|
Token::I8(int_val as i8)
|
|
}
|
|
} else {
|
|
if type_flags.contains(NumericType::BITS_64) {
|
|
Token::U64(int_val)
|
|
} else if type_flags.contains(NumericType::BITS_32) {
|
|
Token::U32(int_val as u32)
|
|
} else if type_flags.contains(NumericType::BITS_16) {
|
|
Token::U16(int_val as u16)
|
|
} else {
|
|
Token::U8(int_val as u8)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if value is truthy
|
|
fn is_truthy(item: &StackValue) -> bool {
|
|
match item {
|
|
StackValue::I64(v) => *v != 0,
|
|
StackValue::I32(v) => *v != 0,
|
|
StackValue::I16(v) => *v != 0,
|
|
StackValue::I8(v) => *v != 0,
|
|
StackValue::U64(v) => *v != 0,
|
|
StackValue::U32(v) => *v != 0,
|
|
StackValue::U16(v) => *v != 0,
|
|
StackValue::U8(v) => *v != 0,
|
|
StackValue::F32(v) => *v != 0.0,
|
|
StackValue::F64(v) => *v != 0.0,
|
|
StackValue::Character(v) => *v != 0,
|
|
StackValue::Boolean(v) => *v,
|
|
StackValue::TokenString(ts) => {
|
|
!ts.tokens.is_empty() && !matches!(ts.tokens[0], Token::Eof)
|
|
}
|
|
StackValue::Callable(_) => false,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
// Extract scalar value (for indexing operations)
|
|
fn extract_scalar(item: &StackValue) -> Option<usize> {
|
|
match item {
|
|
StackValue::I64(v) if *v >= 0 => Some(*v as usize),
|
|
StackValue::I32(v) if *v >= 0 => Some(*v as usize),
|
|
StackValue::I16(v) if *v >= 0 => Some(*v as usize),
|
|
StackValue::I8(v) if *v >= 0 => Some(*v as usize),
|
|
StackValue::U64(v) => Some(*v as usize),
|
|
StackValue::U32(v) => Some(*v as usize),
|
|
StackValue::U16(v) => Some(*v as usize),
|
|
StackValue::U8(v) => Some(*v as usize),
|
|
StackValue::Character(v) => Some(*v as usize),
|
|
StackValue::Boolean(v) => Some(*v as usize),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
// Builtin function implementations
|
|
|
|
pub fn builtin_addition(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = if type_flags.contains(NumericType::FLOAT) {
|
|
numeric_to_token(b.float_val + a.float_val, 0, type_flags)
|
|
} else {
|
|
numeric_to_token(0.0, b.int_val.wrapping_add(a.int_val), type_flags)
|
|
};
|
|
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_subtraction(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = if type_flags.contains(NumericType::FLOAT) {
|
|
numeric_to_token(b.float_val - a.float_val, 0, type_flags)
|
|
} else {
|
|
numeric_to_token(0.0, b.int_val.wrapping_sub(a.int_val), type_flags)
|
|
};
|
|
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_multiplication(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = if type_flags.contains(NumericType::FLOAT) {
|
|
numeric_to_token(b.float_val * a.float_val, 0, type_flags)
|
|
} else {
|
|
numeric_to_token(0.0, b.int_val.wrapping_mul(a.int_val), type_flags)
|
|
};
|
|
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_division(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if a.float_val == 0.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(b.float_val / a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_modulus(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) || a.int_val == 0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = numeric_to_token(0.0, b.int_val % a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_exponential(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(b.float_val.powf(a.float_val)));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_greater_than(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val > a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_greater_than_or_equal_to(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val >= a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_less_than(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val < a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_less_than_or_equal_to(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val <= a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_equal_to(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val == a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_not_equal_to(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(b.float_val != a.float_val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_abs(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if !val.type_flags.contains(NumericType::SIGNED | NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
|
|
let result = if val.type_flags.contains(NumericType::FLOAT) {
|
|
numeric_to_token(val.float_val.abs(), 0, val.type_flags)
|
|
} else {
|
|
numeric_to_token(0.0, (val.int_val as i64).unsigned_abs(), val.type_flags)
|
|
};
|
|
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_acos(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.float_val < -1.0 || val.float_val > 1.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.acos()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_and(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = if type_flags.contains(NumericType::FLOAT) {
|
|
b.float_val != 0.0 && a.float_val != 0.0
|
|
} else {
|
|
b.int_val != 0 && a.int_val != 0
|
|
};
|
|
|
|
state.stack.push(StackValue::Boolean(result));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_asin(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.float_val < -1.0 || val.float_val > 1.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.asin()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_atan(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.atan()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_atan2(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, _)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(b.float_val.atan2(a.float_val)));
|
|
true
|
|
}
|
|
|
|
// Bitwise operations
|
|
pub fn builtin_bitand(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, b.int_val & a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_bitnot(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, !val.int_val, val.type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_bitor(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, b.int_val | a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_bitxor(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, b.int_val ^ a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
// Math functions
|
|
pub fn builtin_ceil(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.ceil()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_floor(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.floor()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_round(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.round()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_sqrt(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.float_val < 0.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.sqrt()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_sin(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.sin()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_cos(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.cos()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_tan(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.tan()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_ln(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.float_val <= 0.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.ln()));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_log(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.float_val <= 0.0 {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::F64(val.float_val.log10()));
|
|
true
|
|
}
|
|
|
|
// Stack operations
|
|
pub fn builtin_depth(state: &mut InterpreterState) -> bool {
|
|
state.stack.push(StackValue::U64(state.stack.len() as u64));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_drop(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
state.stack.pop();
|
|
true
|
|
}
|
|
|
|
pub fn builtin_dup(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
let item = state.stack[state.stack.len()-1].clone();
|
|
state.stack.push(item);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_swap(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 2 {
|
|
return false;
|
|
}
|
|
state.stack.swap(0, 1);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_pick(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(index) = extract_scalar(&state.stack[state.stack.len()-1]) else {
|
|
return false;
|
|
};
|
|
|
|
if index + 1 >= state.stack.len() {
|
|
return false;
|
|
}
|
|
|
|
let item = state.stack[index + 1].clone();
|
|
state.stack.pop();
|
|
state.stack.push(item);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_roll(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 2 {
|
|
return false;
|
|
}
|
|
|
|
let Some(times) = extract_scalar(&state.stack[state.stack.len()-1]) else {
|
|
return false;
|
|
};
|
|
|
|
let Some(count) = extract_scalar(&state.stack[state.stack.len()-2]) else {
|
|
return false;
|
|
};
|
|
|
|
if count == 0 || count + 2 > state.stack.len() {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
// Rotate the top 'count' items 'times' times
|
|
for _ in 0..times {
|
|
let item = state.stack.remove(count - 1);
|
|
state.stack.push(item);
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
// Logical operations
|
|
pub fn builtin_not(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let truthy = is_truthy(&state.stack[state.stack.len()-1]);
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Boolean(!truthy));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_or(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
let result = if type_flags.contains(NumericType::FLOAT) {
|
|
b.float_val != 0.0 || a.float_val != 0.0
|
|
} else {
|
|
b.int_val != 0 || a.int_val != 0
|
|
};
|
|
|
|
state.stack.push(StackValue::Boolean(result));
|
|
true
|
|
}
|
|
|
|
// Shift operations
|
|
pub fn builtin_shl(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, b.int_val << a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
pub fn builtin_shr(state: &mut InterpreterState) -> bool {
|
|
let Some((a, b, type_flags)) = extract_numeric_types(state) else {
|
|
return false;
|
|
};
|
|
|
|
if type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
let result = numeric_to_token(0.0, b.int_val >> a.int_val, type_flags);
|
|
state.push_token(&result);
|
|
true
|
|
}
|
|
|
|
// Random operations
|
|
pub fn builtin_rand(state: &mut InterpreterState) -> bool {
|
|
use rand::Rng;
|
|
let val = rand::thread_rng().gen::<f64>();
|
|
state.stack.push(StackValue::F64(val));
|
|
true
|
|
}
|
|
|
|
pub fn builtin_seed(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let Some(last) = state.stack.last() else { return false };
|
|
let Some(val) = extract_numeric_type(last) else { return false };
|
|
|
|
if val.type_flags.contains(NumericType::FLOAT) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
// Note: Rust's rand crate doesn't have global seeding
|
|
// You'd need to store the RNG in InterpreterState
|
|
true
|
|
}
|
|
|
|
// Control flow operations
|
|
pub fn builtin_if(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 3 {
|
|
return false;
|
|
}
|
|
|
|
let else_block = match &state.stack[state.stack.len()-1] {
|
|
StackValue::TokenString(ts) | StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
let then_block = match &state.stack[state.stack.len()-2] {
|
|
StackValue::TokenString(ts) | StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
let condition = is_truthy(&state.stack[state.stack.len()-3]);
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
if condition {
|
|
state.execute_token_string(&then_block)
|
|
} else {
|
|
state.execute_token_string(&else_block)
|
|
}
|
|
}
|
|
|
|
pub fn builtin_while(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 2 {
|
|
return false;
|
|
}
|
|
|
|
let while_block = match &state.stack[state.stack.len()-1] {
|
|
StackValue::TokenString(ts) | StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
let cond_block = match &state.stack[state.stack.len()-2] {
|
|
StackValue::TokenString(ts) | StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
loop {
|
|
if !state.execute_token_string(&cond_block) {
|
|
return false;
|
|
}
|
|
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let condition = is_truthy(&state.stack[state.stack.len()-1]);
|
|
state.stack.pop();
|
|
|
|
if !condition {
|
|
break;
|
|
}
|
|
|
|
if !state.execute_token_string(&while_block) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
pub fn builtin_for(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 3 {
|
|
return false;
|
|
}
|
|
|
|
let for_block = match &state.stack[state.stack.len()-1] {
|
|
StackValue::TokenString(ts) | StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
let Some(end) = extract_scalar(&state.stack[state.stack.len()-2]) else {
|
|
return false;
|
|
};
|
|
|
|
let Some(start) = extract_scalar(&state.stack[state.stack.len()-3]) else {
|
|
return false;
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
for i in start..end {
|
|
state.stack.push(StackValue::I64(i as i64));
|
|
|
|
if !state.execute_token_string(&for_block) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
pub fn builtin_lambda(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
let stack_len = state.stack.len();
|
|
|
|
match &mut state.stack[stack_len-1] {
|
|
StackValue::TokenString(ts) => {
|
|
let callable = StackValue::Callable(ts.clone());
|
|
state.stack[stack_len-1] = callable;
|
|
true
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn builtin_eval(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let code = state.stack.pop();
|
|
|
|
match code {
|
|
Some(StackValue::TokenString(ts)) | Some(StackValue::Callable(ts)) => {
|
|
state.execute_token_string(&ts)
|
|
}
|
|
Some(StackValue::Identifier(id)) => {
|
|
state.execute_func(&id.name)
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn builtin_const(state: &mut InterpreterState) -> bool {
|
|
if state.stack.len() < 2 {
|
|
return false;
|
|
}
|
|
|
|
let name = match &state.stack[state.stack.len()-1] {
|
|
StackValue::Identifier(id) => id.name.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
let value = match &state.stack[state.stack.len()-2] {
|
|
StackValue::Callable(ts) => ts.clone(),
|
|
_ => return false,
|
|
};
|
|
|
|
if state.functions.contains_key(&name) {
|
|
return false;
|
|
}
|
|
|
|
state.stack.pop();
|
|
state.stack.pop();
|
|
|
|
state.functions.insert(name, FunctionItem::TokenString(value));
|
|
|
|
true
|
|
}
|
|
|
|
pub fn builtin_type_of(state: &mut InterpreterState) -> bool {
|
|
if state.stack.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
let type_name = match &state.stack[state.stack.len()-1] {
|
|
StackValue::Identifier(_) => "Identifier",
|
|
StackValue::I64(_) => "i64",
|
|
StackValue::I32(_) => "i32",
|
|
StackValue::I16(_) => "i16",
|
|
StackValue::I8(_) => "i8",
|
|
StackValue::U64(_) => "u64",
|
|
StackValue::U32(_) => "u32",
|
|
StackValue::U16(_) => "u16",
|
|
StackValue::U8(_) => "u8",
|
|
StackValue::F32(_) => "f32",
|
|
StackValue::F64(_) => "f64",
|
|
StackValue::Character(_) => "char",
|
|
StackValue::Boolean(_) => "bool",
|
|
StackValue::TokenString(_) => "TokenString",
|
|
StackValue::Callable(_) => "Callable",
|
|
};
|
|
|
|
state.stack.pop();
|
|
state.stack.push(StackValue::Identifier(Identifier {
|
|
name: type_name.to_string(),
|
|
is_literal: true,
|
|
}));
|
|
|
|
true
|
|
}
|
|
|
|
pub fn lookup_builtin(name: &str) -> Option<BuiltinFn> {
|
|
if name == "+" { Some(builtin_addition) }
|
|
else if name == "-" { Some(builtin_subtraction) }
|
|
else if name == "*" { Some(builtin_multiplication) }
|
|
else if name == "/" { Some(builtin_division) }
|
|
else if name == "%" { Some(builtin_modulus) }
|
|
else if name == "^" { Some(builtin_exponential) }
|
|
else if name == ">" { Some(builtin_greater_than) }
|
|
else if name == ">=" { Some(builtin_greater_than_or_equal_to) }
|
|
else if name == "<" { Some(builtin_less_than) }
|
|
else if name == "<=" { Some(builtin_less_than_or_equal_to) }
|
|
else if name == "==" { Some(builtin_equal_to) }
|
|
else if name == "!=" { Some(builtin_not_equal_to) }
|
|
else if name == "abs" { Some(builtin_abs) }
|
|
else if name == "acos" { Some(builtin_acos) }
|
|
else if name == "asin" { Some(builtin_asin) }
|
|
else if name == "atan" { Some(builtin_atan) }
|
|
else if name == "atan2" { Some(builtin_atan2) }
|
|
else if name == "ceil" { Some(builtin_ceil) }
|
|
else if name == "floor" { Some(builtin_floor) }
|
|
else if name == "round" { Some(builtin_round) }
|
|
else if name == "sqrt" { Some(builtin_sqrt) }
|
|
else if name == "sin" { Some(builtin_sin) }
|
|
else if name == "cos" { Some(builtin_cos) }
|
|
else if name == "tan" { Some(builtin_tan) }
|
|
else if name == "ln" { Some(builtin_ln) }
|
|
else if name == "log" { Some(builtin_log) }
|
|
else if name == "and" { Some(builtin_and) }
|
|
else if name == "or" { Some(builtin_or) }
|
|
else if name == "not" { Some(builtin_not) }
|
|
else if name == "bitand" { Some(builtin_bitand) }
|
|
else if name == "bitnot" { Some(builtin_bitnot) }
|
|
else if name == "bitor" { Some(builtin_bitor) }
|
|
else if name == "bitxor" { Some(builtin_bitxor) }
|
|
else if name == "shl" { Some(builtin_shl) }
|
|
else if name == "shr" { Some(builtin_shr) }
|
|
else if name == "depth" { Some(builtin_depth) }
|
|
else if name == "drop" { Some(builtin_drop) }
|
|
else if name == "dup" { Some(builtin_dup) }
|
|
else if name == "swap" { Some(builtin_swap) }
|
|
else if name == "pick" { Some(builtin_pick) }
|
|
else if name == "roll" { Some(builtin_roll) }
|
|
else if name == "if" { Some(builtin_if) }
|
|
else if name == "while" { Some(builtin_while) }
|
|
else if name == "for" { Some(builtin_for) }
|
|
else if name == "lambda" { Some(builtin_lambda) }
|
|
else if name == "eval" { Some(builtin_eval) }
|
|
else if name == "const" { Some(builtin_const) }
|
|
else if name == "type_of" { Some(builtin_type_of) }
|
|
else if name == "rand" { Some(builtin_rand) }
|
|
else if name == "seed" { Some(builtin_seed) }
|
|
else { None }
|
|
}
|
|
|
|
// Helper to add builtin to function table
|
|
fn register_builtin(
|
|
functions: &mut HashMap<String, FunctionItem>,
|
|
name: &str,
|
|
func: fn(&mut InterpreterState) -> bool,
|
|
) {
|
|
functions.insert(name.to_string(), FunctionItem::Builtin(BuiltinFunction {
|
|
name: name.to_string(),
|
|
function: match lookup_builtin(name) {
|
|
Some(func) => func,
|
|
None => panic!("Builtin Not Found!"),
|
|
}
|
|
}));
|
|
}
|
|
|
|
pub fn load_builtins(state: &mut InterpreterState) -> bool {
|
|
// Arithmetic
|
|
register_builtin(&mut state.functions, "+", builtin_addition);
|
|
register_builtin(&mut state.functions, "-", builtin_subtraction);
|
|
register_builtin(&mut state.functions, "*", builtin_multiplication);
|
|
register_builtin(&mut state.functions, "/", builtin_division);
|
|
register_builtin(&mut state.functions, "%", builtin_modulus);
|
|
register_builtin(&mut state.functions, "^", builtin_exponential);
|
|
|
|
// Comparison
|
|
register_builtin(&mut state.functions, ">", builtin_greater_than);
|
|
register_builtin(&mut state.functions, ">=", builtin_greater_than_or_equal_to);
|
|
register_builtin(&mut state.functions, "<", builtin_less_than);
|
|
register_builtin(&mut state.functions, "<=", builtin_less_than_or_equal_to);
|
|
register_builtin(&mut state.functions, "==", builtin_equal_to);
|
|
register_builtin(&mut state.functions, "!=", builtin_not_equal_to);
|
|
|
|
// Math functions
|
|
register_builtin(&mut state.functions, "abs", builtin_abs);
|
|
register_builtin(&mut state.functions, "acos", builtin_acos);
|
|
register_builtin(&mut state.functions, "asin", builtin_asin);
|
|
register_builtin(&mut state.functions, "atan", builtin_atan);
|
|
register_builtin(&mut state.functions, "atan2", builtin_atan2);
|
|
register_builtin(&mut state.functions, "ceil", builtin_ceil);
|
|
register_builtin(&mut state.functions, "floor", builtin_floor);
|
|
register_builtin(&mut state.functions, "round", builtin_round);
|
|
register_builtin(&mut state.functions, "sqrt", builtin_sqrt);
|
|
register_builtin(&mut state.functions, "sin", builtin_sin);
|
|
register_builtin(&mut state.functions, "cos", builtin_cos);
|
|
register_builtin(&mut state.functions, "tan", builtin_tan);
|
|
register_builtin(&mut state.functions, "ln", builtin_ln);
|
|
register_builtin(&mut state.functions, "log", builtin_log);
|
|
|
|
// Logical
|
|
register_builtin(&mut state.functions, "and", builtin_and);
|
|
register_builtin(&mut state.functions, "or", builtin_or);
|
|
register_builtin(&mut state.functions, "not", builtin_not);
|
|
|
|
// Bitwise
|
|
register_builtin(&mut state.functions, "bitand", builtin_bitand);
|
|
register_builtin(&mut state.functions, "bitnot", builtin_bitnot);
|
|
register_builtin(&mut state.functions, "bitor", builtin_bitor);
|
|
register_builtin(&mut state.functions, "bitxor", builtin_bitxor);
|
|
register_builtin(&mut state.functions, "shl", builtin_shl);
|
|
register_builtin(&mut state.functions, "shr", builtin_shr);
|
|
|
|
// Stack operations
|
|
register_builtin(&mut state.functions, "depth", builtin_depth);
|
|
register_builtin(&mut state.functions, "drop", builtin_drop);
|
|
register_builtin(&mut state.functions, "dup", builtin_dup);
|
|
register_builtin(&mut state.functions, "swap", builtin_swap);
|
|
register_builtin(&mut state.functions, "pick", builtin_pick);
|
|
register_builtin(&mut state.functions, "roll", builtin_roll);
|
|
|
|
// Control flow
|
|
register_builtin(&mut state.functions, "if", builtin_if);
|
|
register_builtin(&mut state.functions, "while", builtin_while);
|
|
register_builtin(&mut state.functions, "for", builtin_for);
|
|
register_builtin(&mut state.functions, "lambda", builtin_lambda);
|
|
register_builtin(&mut state.functions, "eval", builtin_eval);
|
|
register_builtin(&mut state.functions, "const", builtin_const);
|
|
|
|
// Utility
|
|
register_builtin(&mut state.functions, "type_of", builtin_type_of);
|
|
register_builtin(&mut state.functions, "rand", builtin_rand);
|
|
register_builtin(&mut state.functions, "seed", builtin_seed);
|
|
|
|
// Load SLS-defined builtins
|
|
let builtin_code = r#"
|
|
// 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
|
|
"#;
|
|
|
|
use crate::lexer::{LexerInfo, lexical_analysis};
|
|
|
|
let mut lexer = LexerInfo::new("__builtin.sls", builtin_code);
|
|
|
|
match lexical_analysis(&mut lexer) {
|
|
Ok(tokens) => {
|
|
for token in tokens {
|
|
if matches!(token, Token::Eof) {
|
|
break;
|
|
}
|
|
if !state.execute(&token) {
|
|
eprintln!("Error in __builtin.sls");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
dbg!(e);
|
|
return false
|
|
}
|
|
}
|
|
|
|
true
|
|
}
|