diff --git a/SLS_Rust/sls/Cargo.toml b/SLS_Rust/sls/Cargo.toml new file mode 100644 index 0000000..7d3fec8 --- /dev/null +++ b/SLS_Rust/sls/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sls_rs" +version = "0.0.1-alpha" +edition = "2021" + +[dependencies] +bitflags = "2.4" +rand = "0.8" + +[build-dependencies] +rustc_version = "0.4" +chrono = "0.4" +vergen = { version = "8", features = ["build"] } diff --git a/SLS_Rust/sls/build.rs b/SLS_Rust/sls/build.rs new file mode 100644 index 0000000..1bf2e81 --- /dev/null +++ b/SLS_Rust/sls/build.rs @@ -0,0 +1,45 @@ +use std::process::Command; +use vergen::EmitBuilder; + +fn try_cmd(cmd: &mut Command) -> Option { + let out = cmd.output().ok()?; + if !out.status.success() { + return None; + } + Some(String::from_utf8_lossy(&out.stdout).trim().to_string()) +} + +fn main() { + // Emit all default vergen build info (BUILD_DATE / BUILD_TIME, etc.) + EmitBuilder::builder().all_build(); + + // Git describe + commit date (matches your Python logic) + let commit_info = (|| { + let hash = try_cmd( + Command::new("git") + .arg("describe") + .arg("--always") + .arg("--dirty") + .arg("--abbrev=7"), + )?; + + let date = try_cmd( + Command::new("git") + .arg("show") + .arg("-s") + .arg("--format=%ci"), + )?; + + Some(format!("{} {}", hash, date)) + })() + .unwrap_or_else(|| "unknown".into()); + + println!("cargo:rustc-env=GIT_COMMIT_HASH={}", commit_info); + + // Compiler info + println!("cargo:rustc-env=COMPILER_NAME=rustc"); + + let rustc_ver = try_cmd(Command::new("rustc").arg("--version")) + .unwrap_or_else(|| "unknown".into()); + println!("cargo:rustc-env=COMPILER_VER={}", rustc_ver); +} diff --git a/SLS_Rust/sls/core b/SLS_Rust/sls/core new file mode 100644 index 0000000..d863bd6 Binary files /dev/null and b/SLS_Rust/sls/core differ diff --git a/SLS_Rust/sls/src/builtin.rs b/SLS_Rust/sls/src/builtin.rs new file mode 100644 index 0000000..37a3cfd --- /dev/null +++ b/SLS_Rust/sls/src/builtin.rs @@ -0,0 +1,1120 @@ +use crate::interpreter::{InterpreterState, StackValue, FunctionItem}; +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 { + 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 { + 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::(); + 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 +} + +// Helper to add builtin to function table +fn register_builtin( + functions: &mut HashMap, + name: &str, + func: fn(&mut InterpreterState) -> bool, +) { + functions.insert(name.to_string(), FunctionItem::Builtin(func)); +} + +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 +} diff --git a/SLS_Rust/sls/src/file.rs b/SLS_Rust/sls/src/file.rs new file mode 100644 index 0000000..5539174 --- /dev/null +++ b/SLS_Rust/sls/src/file.rs @@ -0,0 +1,52 @@ +use std::fs; + +use crate::interpreter::InterpreterState; +use crate::lexer::{LexerInfo, lexical_analysis, LexResult}; + +/// Execute the contents of a script file. +pub fn exec_file(interpreter: &mut InterpreterState, filename: &str) -> bool { + // Read the whole file + let source = match fs::read_to_string(filename) { + Ok(s) => s, + Err(e) => { + eprintln!("Cannot read file: {} ({})", filename, e); + return false; + } + }; + + let mut lexer_info = LexerInfo::new(filename, source.clone()); + + let result = lexical_analysis(&mut lexer_info); + + match result { + LexResult::Ok(tokens) => { + for token in tokens { + if !interpreter.execute(&token) { + return false; + } + } + true + } + + LexResult::Err(err) => { + dbg!(err); + false + } + } +} + +/// Stand-alone file execution entry point. +pub fn run_file(filename: &str) -> i32 { + println!("Executing file: {}", filename); + + let mut interpreter = InterpreterState::new(); + if !interpreter.init() { + return 1; + } + + if exec_file(&mut interpreter, filename) { + 0 + } else { + 1 + } +} diff --git a/SLS_Rust/sls/src/interpreter.rs b/SLS_Rust/sls/src/interpreter.rs new file mode 100644 index 0000000..9c7bfc4 --- /dev/null +++ b/SLS_Rust/sls/src/interpreter.rs @@ -0,0 +1,134 @@ +use std::collections::HashMap; + +use crate::lexer::*; // Identifier, Token, TokenString, etc. +use crate::builtin::load_builtins; + +pub type BuiltinFn = fn(&mut InterpreterState) -> bool; + +#[derive(Debug, Clone)] +pub enum StackValue { + Identifier(Identifier), + + I64(i64), + I32(i32), + I16(i16), + I8(i8), + + U64(u64), + U32(u32), + U16(u16), + U8(u8), + + F32(f32), + F64(f64), + + Character(u8), + Boolean(bool), + + TokenString(TokenString), + + Callable(TokenString), +} + +#[derive(Debug, Clone)] +pub enum FunctionItem { + TokenString(TokenString), + Builtin(BuiltinFn), +} + +#[derive(Debug)] +pub struct InterpreterState { + pub stack: Vec, + pub functions: HashMap, +} + +impl InterpreterState { + pub fn new() -> Self { + Self { + stack: Vec::new(), + functions: HashMap::new(), + } + } + + pub fn init(&mut self) -> bool { + load_builtins(self) + } + + pub fn push_token(&mut self, token: &Token) -> bool { + let value = match token { + Token::Eof => return true, + + Token::Identifier(id) => { + StackValue::Identifier(id.clone()) + } + + Token::I64(v) => StackValue::I64(*v), + Token::I32(v) => StackValue::I32(*v), + Token::I16(v) => StackValue::I16(*v), + Token::I8(v) => StackValue::I8(*v), + + Token::U64(v) => StackValue::U64(*v), + Token::U32(v) => StackValue::U32(*v), + Token::U16(v) => StackValue::U16(*v), + Token::U8(v) => StackValue::U8(*v), + + Token::Float(v) => StackValue::F32(*v), + Token::Double(v) => StackValue::F64(*v), + + Token::Character(c) => StackValue::Character(*c), + Token::Boolean(b) => StackValue::Boolean(*b), + + Token::TokenString(ts) => StackValue::TokenString(ts.clone()), + + Token::StringLiteral(_) | + Token::Array(_) | + Token::TypeTuple(_) => return false, + }; + + self.stack.push(value); + true + } + + pub fn execute_func(&mut self, key: &str) -> bool { + let item = match self.functions.get(key) { + Some(v) => v.clone(), + None => return false, + }; + + match item { + FunctionItem::Builtin(f) => f(self), + FunctionItem::TokenString(ts) => self.execute_token_string(&ts), + } + } + + pub fn execute_token_string(&mut self, ts: &TokenString) -> bool { + for token in &ts.tokens { + if let Token::Identifier(id) = &token { + if !id.is_literal { + if !self.execute_func(&id.name) { + return false; + } + continue; + } + } + + if !self.push_token(&token) { + return false; + } + } + true + } + + pub fn execute(&mut self, token: &Token) -> bool { + match token { + Token::Identifier(id) if !id.is_literal => { + self.execute_func(&id.name) + } + _ => self.push_token(token), + } + } + + pub fn stack_top(&self) -> Option<&StackValue> { + self.stack.last() + } +} diff --git a/SLS_Rust/sls/src/lexer.rs b/SLS_Rust/sls/src/lexer.rs new file mode 100644 index 0000000..8b6df6f --- /dev/null +++ b/SLS_Rust/sls/src/lexer.rs @@ -0,0 +1,737 @@ +#[derive(Debug, Clone)] +pub struct LexerInfo { + pub filename: String, + pub source: String, + pub pos: usize, + pub column: usize, + pub line: usize, +} + +impl LexerInfo { + pub fn new(filename: impl Into, source: impl Into) -> Self { + Self { + filename: filename.into(), + source: source.into(), + pos: 0, + column: 1, + line: 1, + } + } + + fn peek(&self) -> char { + self.source.chars().nth(self.pos).unwrap_or('\0') + } + + fn far_peek(&self, offset: usize) -> char { + self.source.chars().nth(self.pos + offset).unwrap_or('\0') + } + + fn advance(&mut self) -> char { + if self.peek() == '\n' { + self.line += 1; + self.column = 1; + } else { + self.column += 1; + } + self.pos += 1; + self.peek() + } + + fn skip_comments_and_whitespace(&mut self) { + loop { + let c = self.peek(); + + // Skip comments + if (c == '/' && self.far_peek(1) == '/') || c == '#' { + while self.peek() != '\n' && self.peek() != '\0' { + self.advance(); + } + } + + // Skip whitespace + if self.peek().is_whitespace() { + while self.peek().is_whitespace() { + self.advance(); + } + } else { + break; + } + } + } +} + +#[derive(Debug, Clone)] +pub struct Identifier { + pub name: String, + pub is_literal: bool, +} + +#[derive(Debug, Clone)] +pub enum ArrayLiteral { + _Identifiers(Vec), + _I64(Vec), + _I32(Vec), + _I16(Vec), + _I8(Vec), + _U64(Vec), + _U32(Vec), + _U16(Vec), + _U8(Vec), + _Float(Vec), + _Double(Vec), + _Character(Vec), + _Strings(Vec), + _Boolean(Vec), + _TokenStrings(Vec), + _TypeTuples(Vec), + _StructInline(StructInline), +} + +#[derive(Debug, Clone)] +pub struct ShapedArray { + pub _array: ArrayLiteral, + pub _shape: Vec, +} + +#[derive(Debug, Clone)] +pub struct TokenString { + pub tokens: Vec, +} + +#[derive(Debug, Clone)] +pub struct TypeTuple { + pub _inputs: Vec, + pub _outputs: Vec, +} + +#[derive(Debug, Clone)] +pub struct StructInline { + pub _name: String, + pub _values: Vec, +} + +#[derive(Debug, Clone)] +pub enum StructValue { + _Integer(i64), + _Float(f32), + _Double(f64), + _Boolean(bool), + _Character(u8), + _String(String), + _Token(Token), +} + +#[derive(Debug, Clone)] +pub enum Token { + Eof, + Identifier(Identifier), + I64(i64), + I32(i32), + I16(i16), + I8(i8), + U64(u64), + U32(u32), + U16(u16), + U8(u8), + Float(f32), + Double(f64), + Character(u8), + StringLiteral(String), + Boolean(bool), + Array(ShapedArray), + TokenString(TokenString), + TypeTuple(TypeTuple), +} + +#[derive(Debug, Clone)] +pub struct LexError { + pub message: String, + pub file: String, + pub line: usize, + pub column: usize, +} + +pub type LexResult = Result; + +#[derive(Debug, Clone, Copy)] +enum NumericLiteralType { + Binary, + Octal, + Decimal, + Hexadecimal, + Float, +} + +impl LexerInfo { + fn make_error(&self, message: impl Into, start_line: usize, start_col: usize) -> LexError { + LexError { + message: message.into(), + file: self.filename.clone(), + line: start_line, + column: start_col, + } + } + + fn is_identifier_continue(&self, c: char) -> bool { + if !c.is_ascii() || !c.is_ascii_graphic() { + return false; + } + if c == '/' && self.far_peek(1) == '/' { + return false; + } + !matches!(c, '{' | '}' | '[' | ']' | '(' | ')' | '\'' | '"' | '#') && !c.is_whitespace() + } + + fn is_identifier_start(&self) -> bool { + let mut c = self.peek(); + if c == ':' && self.far_peek(1) == ':' { + c = self.far_peek(2); + } + !c.is_ascii_digit() && self.is_identifier_continue(c) + } + + fn parse_identifiers_and_booleans(&mut self, _start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + let mut literal = false; + + // Skip leading `::` for identifier literals + if c == ':' && self.far_peek(1) == ':' { + literal = true; + self.advance(); + c = self.advance(); + } + + // Read the name + let name_start = self.pos; + while self.is_identifier_continue(c) { + if c == ':' { + return Err(self.make_error("Invalid identifier: ':' is not allowed in identifiers.", start_line, start_col)); + } + if c == '.' { + return Err(self.make_error("Invalid identifier: '.' is not allowed in identifiers.", start_line, start_col)); + } + c = self.advance(); + } + + let name = self.source[name_start..self.pos].to_string(); + + // Check for booleans + match name.as_str() { + "false" => Ok(Token::Boolean(false)), + "true" => Ok(Token::Boolean(true)), + _ => Ok(Token::Identifier(Identifier { name, is_literal: literal })), + } + } + + fn parse_character_literal(&mut self, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + + if c == '\'' { + return Err(self.make_error("Invalid character literal: empty character literal.", start_line, start_col)); + } + + let value = if c == '\\' { + c = self.advance(); + match c { + 'n' => b'\n', + 'r' => b'\r', + 't' => b'\t', + '\\' => b'\\', + '\'' => b'\'', + '0' => b'\0', + _ => return Err(self.make_error(format!("Invalid character literal: unknown escape sequence '\\{}'.", c), start_line, start_col)), + } + } else if c == '\n' || c == '\r' { + return Err(self.make_error("Invalid character literal: unclosed character literal.", start_line, start_col)); + } else { + c as u8 + }; + + c = self.advance(); + + if c.is_whitespace() || c == '/' || c == '\0' { + return Err(self.make_error("Invalid character literal: unclosed character literal.", start_line, start_col)); + } else if c != '\'' { + return Err(self.make_error(format!("Invalid character literal: unexpected '{}' in character.", c), start_line, start_col)); + } + + self.advance(); + Ok(Token::Character(value)) + } + + fn parse_token_string(&mut self, _start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut tokens = Vec::new(); + self.advance(); // skip '{' + + loop { + self.skip_comments_and_whitespace(); + let c = self.peek(); + + if c == '}' { + self.advance(); + return Ok(Token::TokenString(TokenString { tokens })); + } + + if c == '\0' { + return Err(self.make_error("Unclosed token string: missing closing brace '}'.", start_line, start_col)); + } + + match get_token(self) { + Some(token) => { + if matches!(token, Token::Eof) { + break; + } + tokens.push(token); + } + None => return Err(self.make_error("Failed to parse token in token string.", start_line, start_col)), + } + } + + Err(self.make_error("Unclosed token string: missing closing brace '}'.", start_line, start_col)) + } + + fn parse_numeric_literal(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + + if c == '-' { + c = self.advance(); + } + + if c == '0' { + c = self.advance(); + match c { + 'b' | 'B' => { + self.advance(); + return self.parse_binary_integer(start, start_line, start_col); + } + 'o' | 'O' => { + self.advance(); + return self.parse_octal_integer(start, start_line, start_col); + } + 'x' | 'X' => { + self.advance(); + return self.parse_hexadecimal_integer(start, start_line, start_col); + } + _ => {} + } + } + + self.parse_decimal_integer(start, start_line, start_col) + } + + fn parse_binary_integer(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + while c == '0' || c == '1' || c == '_' { + c = self.advance(); + } + + if c == ':' { + return self.parse_numeric_type(start, start_line, start_col, NumericLiteralType::Binary); + } + + let value = self.create_binary_integer(start); + Ok(Token::I64(value as i64)) + } + + fn parse_octal_integer(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + while c.is_ascii_digit() && c != '8' && c != '9' || c == '_' { + c = self.advance(); + } + + if c == ':' { + return self.parse_numeric_type(start, start_line, start_col, NumericLiteralType::Octal); + } + + let value = self.create_octal_integer(start); + Ok(Token::I64(value as i64)) + } + + fn parse_decimal_integer(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + while c.is_ascii_digit() || c == '_' { + c = self.advance(); + } + + if c == '.' { + self.advance(); + return self.parse_float(start, start_line, start_col); + } + + if c == ':' { + return self.parse_numeric_type(start, start_line, start_col, NumericLiteralType::Decimal); + } + + let value = self.create_decimal_integer(start); + Ok(Token::I64(value as i64)) + } + + fn parse_hexadecimal_integer(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + while c.is_ascii_hexdigit() || c == '_' { + c = self.advance(); + } + + if c == ':' { + return self.parse_numeric_type(start, start_line, start_col, NumericLiteralType::Hexadecimal); + } + + let value = self.create_hexadecimal_integer(start); + Ok(Token::I64(value as i64)) + } + + fn parse_float(&mut self, start: usize, start_line: usize, start_col: usize) -> LexResult { + let mut c = self.peek(); + while c.is_ascii_digit() || c == '_' { + c = self.advance(); + } + + if c == ':' { + return self.parse_numeric_type(start, start_line, start_col, NumericLiteralType::Float); + } + + let value = self.create_float(start); + Ok(Token::Double(value)) + } + + fn parse_numeric_type(&mut self, start: usize, start_line: usize, start_col: usize, literal_type: NumericLiteralType) -> LexResult { + let mut c = self.advance(); // skip ':' + + let mut is_float = false; + let mut is_unsigned = false; + let bit_size: u32; + + if c == 'f' { + is_float = true; + if !matches!(literal_type, NumericLiteralType::Decimal | NumericLiteralType::Float) { + return Err(self.make_error("Invalid numeric literal: float type not allowed.", start_line, start_col)); + } + c = self.advance(); + if c == '6' && self.far_peek(1) == '4' { + bit_size = 64; + self.advance(); + self.advance(); + } else if c == '3' && self.far_peek(1) == '2' { + bit_size = 32; + self.advance(); + self.advance(); + } else { + return Err(self.make_error("Invalid float type: must be of type 'f64' or 'f32'.", start_line, start_col)); + } + } else if c == 'i' || c == 'u' { + if matches!(literal_type, NumericLiteralType::Float) { + return Err(self.make_error("Invalid float type: must be of type 'f64' or 'f32'.", start_line, start_col)); + } + is_unsigned = c == 'u'; + c = self.advance(); + if c == '6' && self.far_peek(1) == '4' { + bit_size = 64; + self.advance(); + self.advance(); + } else if c == '3' && self.far_peek(1) == '2' { + bit_size = 32; + self.advance(); + self.advance(); + } else if c == '1' && self.far_peek(1) == '6' { + bit_size = 16; + self.advance(); + self.advance(); + } else if c == '8' { + bit_size = 8; + self.advance(); + } else { + let type_name = if is_unsigned { "unsigned" } else { "signed" }; + return Err(self.make_error( + format!("Invalid {} integer type: must be of type '{}64', '{}32', '{}16', or '{}8'.", + type_name, if is_unsigned { "u" } else { "i" }, + if is_unsigned { "u" } else { "i" }, + if is_unsigned { "u" } else { "i" }, + if is_unsigned { "u" } else { "i" }), + start_line, start_col)); + } + } else { + return Err(self.make_error("Invalid numeric type: type must start with 'f', 'i', or 'u'.", start_line, start_col)); + } + + // Create the token based on the parsed type + if is_float { + let value = self.create_float(start); + match bit_size { + 32 => Ok(Token::Float(value as f32)), + 64 => Ok(Token::Double(value)), + _ => unreachable!() + } + } else { + let value = match literal_type { + NumericLiteralType::Binary => self.create_binary_integer(start), + NumericLiteralType::Octal => self.create_octal_integer(start), + NumericLiteralType::Decimal => self.create_decimal_integer(start), + NumericLiteralType::Hexadecimal => self.create_hexadecimal_integer(start), + NumericLiteralType::Float => return Err(self.make_error("Internal error: float literal in integer path", start_line, start_col)), + }; + + self.create_integer_token(value, is_unsigned, bit_size, start, start_line, start_col) + } + } + + fn create_integer_token(&self, value: u64, is_unsigned: bool, bit_size: u32, start: usize, start_line: usize, start_col: usize) -> LexResult { + let is_negative = self.source[start..].starts_with('-'); + + match (is_unsigned, bit_size) { + (false, 64) => Ok(Token::I64(value as i64)), + (false, 32) => { + let signed = value as i64; + if signed < i32::MIN as i64 || signed > i32::MAX as i64 { + return Err(self.make_error("Integer overflow: value exceeds range for i32.", start_line, start_col)); + } + Ok(Token::I32(value as i32)) + } + (false, 16) => { + let signed = value as i64; + if signed < i16::MIN as i64 || signed > i16::MAX as i64 { + return Err(self.make_error("Integer overflow: value exceeds range for i16.", start_line, start_col)); + } + Ok(Token::I16(value as i16)) + } + (false, 8) => { + let signed = value as i64; + if signed < i8::MIN as i64 || signed > i8::MAX as i64 { + return Err(self.make_error("Integer overflow: value exceeds range for i8.", start_line, start_col)); + } + Ok(Token::I8(value as i8)) + } + (true, 64) => { + if is_negative { + return Err(self.make_error("Integer overflow: value exceeds range for u64.", start_line, start_col)); + } + Ok(Token::U64(value)) + } + (true, 32) => { + if is_negative { + return Err(self.make_error("Integer overflow: value exceeds range for u32.", start_line, start_col)); + } + if value > u32::MAX as u64 { + return Err(self.make_error("Integer overflow: value exceeds range for u32.", start_line, start_col)); + } + Ok(Token::U32(value as u32)) + } + (true, 16) => { + if is_negative { + return Err(self.make_error("Integer overflow: value exceeds range for u16.", start_line, start_col)); + } + if value > u16::MAX as u64 { + return Err(self.make_error("Integer overflow: value exceeds range for u16.", start_line, start_col)); + } + Ok(Token::U16(value as u16)) + } + (true, 8) => { + if is_negative { + return Err(self.make_error("Integer overflow: value exceeds range for u8.", start_line, start_col)); + } + if value > u8::MAX as u64 { + return Err(self.make_error("Integer overflow: value exceeds range for u8.", start_line, start_col)); + } + Ok(Token::U8(value as u8)) + } + _ => Err(self.make_error("Invalid bit size for integer type.", start_line, start_col)) + } + } + + fn create_binary_integer(&self, start: usize) -> u64 { + let token = &self.source[start..self.pos]; + let mut value = 0u64; + let mut i = 2; + + if token.starts_with('-') { + i += 1; + } + + for c in token[i..].chars() { + if c == '_' || c == '.' { + continue; + } + if c.is_whitespace() || c == '/' || c == ':' { + break; + } + value *= 2; + if c == '1' { + value += 1; + } + } + + if token.starts_with('-') { + (!value).wrapping_add(1) + } else { + value + } + } + + fn create_octal_integer(&self, start: usize) -> u64 { + let token = &self.source[start..self.pos]; + let mut value = 0u64; + let mut i = 2; + + if token.starts_with('-') { + i += 1; + } + + for c in token[i..].chars() { + if c == '_' || c == '.' { + continue; + } + if c.is_whitespace() || c == '/' || c == ':' { + break; + } + value *= 8; + value += c.to_digit(8).unwrap_or(0) as u64; + } + + if token.starts_with('-') { + (!value).wrapping_add(1) + } else { + value + } + } + + fn create_decimal_integer(&self, start: usize) -> u64 { + let token = &self.source[start..self.pos]; + let mut value = 0u64; + let mut i = 0; + + if token.starts_with('-') { + i += 1; + } + + for c in token[i..].chars() { + if c == '_' { + continue; + } + if c.is_whitespace() || c == '/' || c == ':' { + break; + } + value *= 10; + value += c.to_digit(10).unwrap_or(0) as u64; + } + + if token.starts_with('-') { + (!value).wrapping_add(1) + } else { + value + } + } + + fn create_hexadecimal_integer(&self, start: usize) -> u64 { + let token = &self.source[start..self.pos]; + let mut value = 0u64; + let mut i = 2; + + if token.starts_with('-') { + i += 1; + } + + for c in token[i..].chars() { + if c == '_' || c == '.' { + continue; + } + if c.is_whitespace() || c == '/' || c == ':' { + break; + } + value *= 16; + value += c.to_digit(16).unwrap_or(0) as u64; + } + + if token.starts_with('-') { + (!value).wrapping_add(1) + } else { + value + } + } + + fn create_float(&self, start: usize) -> f64 { + let token = &self.source[start..self.pos]; + let mut value = 0.0; + let mut fractional = 0u64; + let mut i = 0; + + if token.starts_with('-') { + i += 1; + } + + for c in token[i..].chars() { + if c == '_' { + continue; + } + if c.is_whitespace() || c == '/' || c == ':' { + break; + } + if c == '.' { + fractional = 1; + continue; + } + + if fractional == 0 { + value *= 10.0; + } else { + fractional *= 10; + } + + let digit = c.to_digit(10).unwrap_or(0) as f64; + if fractional == 0 { + value += digit; + } else { + value += digit / fractional as f64; + } + } + + if token.starts_with('-') { + -value + } else { + value + } + } +} + +pub fn get_token(lexer: &mut LexerInfo) -> Option { + lexer.skip_comments_and_whitespace(); + + let c = lexer.peek(); + let start = lexer.pos; + let start_line = lexer.line; + let start_col = lexer.column; + + if c == '\0' { + return Some(Token::Eof); + } + + let result = if c.is_ascii_digit() || (c == '.' && lexer.far_peek(1).is_ascii_digit()) || (c == '-' && lexer.far_peek(1).is_ascii_digit()) { + lexer.parse_numeric_literal(start, start_line, start_col) + } else if c == '\'' { + lexer.advance(); + lexer.parse_character_literal(start_line, start_col) + } else if c == '{' { + lexer.parse_token_string(start, start_line, start_col) + } else if lexer.is_identifier_start() { + lexer.parse_identifiers_and_booleans(start, start_line, start_col) + } else { + Err(lexer.make_error(format!("Unexpected character: '{}'", c), start_line, start_col)) + }; + + result.ok() +} + +pub fn lexical_analysis(lexer: &mut LexerInfo) -> LexResult> { + let mut tokens = Vec::new(); + + loop { + match get_token(lexer) { + Some(Token::Eof) => { + tokens.push(Token::Eof); + break; + } + Some(token) => tokens.push(token), + None => break, + } + } + + Ok(tokens) +} diff --git a/SLS_Rust/sls/src/main.rs b/SLS_Rust/sls/src/main.rs new file mode 100644 index 0000000..f7a871a --- /dev/null +++ b/SLS_Rust/sls/src/main.rs @@ -0,0 +1,63 @@ +mod builtin; +mod file; +mod interpreter; +mod lexer; +mod repl; + +use std::env; +use std::process; + +use file::run_file; +use repl::repl; + +// These mirror the C macros. +const SLS_NAME: &str = "SLS_RUST"; +const SLS_VER: &str = "a.0.0"; + +pub fn print_version() { + let git_hash = option_env!("GIT_COMMIT_HASH").unwrap_or("unknown"); + let compiler = option_env!("COMPILER_NAME").unwrap_or("rustc"); + let compiler_ver = option_env!("COMPILER_VER").unwrap_or("unknown"); + let build_date = std::env::var("BUILD_DATE").unwrap_or_else(|_| "unknown".into()); + let build_time = std::env::var("BUILD_TIME").unwrap_or_else(|_| "unknown".into()); + + println!("YREA SLS ({SLS_NAME}) {SLS_VER} ({git_hash})"); + println!("Compiled with {compiler} {compiler_ver} at {build_date} {build_time}"); +} + +fn main() { + let mut args = env::args().skip(1); + + let mut version_flag = false; + let mut filename: Option = None; + + match args.len() { + 0 => {} + 1 => { + let arg = args.next().unwrap(); + if arg == "--version" || arg == "-v" { + version_flag = true; + } else { + filename = Some(arg); + } + } + _ => { + eprintln!("Too many arguments!"); + process::exit(1); + } + } + + if version_flag { + print_version(); + process::exit(0); + } + + if let Some(file) = filename { + let status = run_file(&file); + process::exit(status); + } + + // Default to REPL + let status = repl(); + process::exit(status); +} diff --git a/SLS_Rust/sls/src/repl.rs b/SLS_Rust/sls/src/repl.rs new file mode 100644 index 0000000..32d2728 --- /dev/null +++ b/SLS_Rust/sls/src/repl.rs @@ -0,0 +1,86 @@ +use std::io::{self, Write}; + +use crate::lexer::{LexerInfo, LexResult, lexical_analysis}; +use crate::print_version; +use crate::interpreter::{InterpreterState, StackValue}; + +static REPL_FILE_NAME: &str = ""; + +fn print_top_of_stack(state: &InterpreterState) { + let Some(item) = state.stack_top() else { + println!("#0: "); + return; + }; + + match &item { + StackValue::Identifier(id) => { + println!("#0: ::{}", id.name); + } + StackValue::I64(v) => println!("#0: {}", v), + StackValue::I32(v) => println!("#0: {}:i32", v), + StackValue::I16(v) => println!("#0: {}:i16", v), + StackValue::I8(v) => println!("#0: {}:i8", v), + + StackValue::U64(v) => println!("#0: {}:u64", v), + StackValue::U32(v) => println!("#0: {}:u32", v), + StackValue::U16(v) => println!("#0: {}:u16", v), + StackValue::U8(v) => println!("#0: {}:u8", v), + + StackValue::F32(v) => println!("#0: {}:f32", v), + StackValue::F64(v) => println!("#0: {}", v), + + StackValue::Character(ch) => println!("#0: {}", ch), + StackValue::Boolean(b) => println!("#0: {}", if *b { "TRUE" } else { "FALSE" }), + + StackValue::TokenString(_) => println!("#0: "), + StackValue::Callable(_) => println!("#0: "), + }; +} + +pub fn repl() -> i32 { + print_version(); + println!("===== YREA SLS REPL ====="); + println!("Type `#exit` to exit."); + io::stdout().flush().unwrap(); + + let mut interpreter = InterpreterState::new(); + if !interpreter.init() { + return 1; + } + + let stdin = io::stdin(); + let mut buf = String::new(); + + loop { + buf.clear(); + + if stdin.read_line(&mut buf).is_err() { + return 1; + } + + if buf.trim_end() == "#exit" { + return 0; + } + + let code = buf.clone(); + let mut lexer_info = LexerInfo::new(REPL_FILE_NAME, code.clone()); + let result = lexical_analysis(&mut lexer_info); + + match result { + LexResult::Ok(tokens) => { + for token in tokens { + if !interpreter.execute(&token) { + eprintln!("A runtime error occured!"); + break; + } + } + + print_top_of_stack(&interpreter); + } + + LexResult::Err(err) => { + dbg!(err); + } + } + } +} diff --git a/SLS_Tests/cases.yaml b/SLS_Tests/cases.yaml index 083da64..1dda3a5 100644 --- a/SLS_Tests/cases.yaml +++ b/SLS_Tests/cases.yaml @@ -1993,7 +1993,7 @@ stack_final: - type: f32 value: -0.0 -- name: Char Simple Letter A +- name: Char Simple Letter Uppercase A code: '''A''' tokens: - type: char @@ -2005,7 +2005,7 @@ stack_final: - type: char value: A -- name: Char Simple Letter a +- name: Char Simple Letter Lowercase a code: '''a''' tokens: - type: char @@ -2017,7 +2017,7 @@ stack_final: - type: char value: a -- name: Char Simple Letter Z +- name: Char Simple Letter Uppercase Z code: '''Z''' tokens: - type: char @@ -2029,7 +2029,7 @@ stack_final: - type: char value: Z -- name: Char Simple Letter z +- name: Char Simple Letter Lowercase z code: '''z''' tokens: - type: char @@ -2317,42 +2317,6 @@ stack_final: - type: char value: '}' -- name: Char Escape Tab - code: '''\\t''' - tokens: - - type: char - value: "\t" - operations: - - function: push - type: char - value: "\t" - stack_final: - - type: char - value: "\t" -- name: Char Escape Backslash - code: '''\\\\''' - tokens: - - type: char - value: \ - operations: - - function: push - type: char - value: \ - stack_final: - - type: char - value: \ -- name: Char Escape Null character - code: '''\\0''' - tokens: - - type: char - value: "\0" - operations: - - function: push - type: char - value: "\0" - stack_final: - - type: char - value: "\0" - name: Char Escape Single quote code: '''\\''''' tokens: @@ -2365,18 +2329,6 @@ stack_final: - type: char value: '''' -- name: Char Escape Carriage return - code: '''\\r''' - tokens: - - type: char - value: "\r" - operations: - - function: push - type: char - value: "\r" - stack_final: - - type: char - value: "\r" - name: Char Escape Newline code: '''\\n''' tokens: @@ -2395,6 +2347,54 @@ value: ' ' +- name: Char Escape Null character + code: '''\\0''' + tokens: + - type: char + value: "\0" + operations: + - function: push + type: char + value: "\0" + stack_final: + - type: char + value: "\0" +- name: Char Escape Backslash + code: '''\\\\''' + tokens: + - type: char + value: \ + operations: + - function: push + type: char + value: \ + stack_final: + - type: char + value: \ +- name: Char Escape Tab + code: '''\\t''' + tokens: + - type: char + value: "\t" + operations: + - function: push + type: char + value: "\t" + stack_final: + - type: char + value: "\t" +- name: Char Escape Carriage return + code: '''\\r''' + tokens: + - type: char + value: "\r" + operations: + - function: push + type: char + value: "\r" + stack_final: + - type: char + value: "\r" - name: Char With Leading Whitespace code: ' ''A''' tokens: diff --git a/SLS_Tests/generate_tests/char_tests.py b/SLS_Tests/generate_tests/char_tests.py index f461606..3278e9a 100644 --- a/SLS_Tests/generate_tests/char_tests.py +++ b/SLS_Tests/generate_tests/char_tests.py @@ -28,10 +28,10 @@ class CharTestGenerator(BaseTestGenerator): def generate_basic_tests(self): """Generate basic character literal tests.""" # Simple ASCII letters - self.make_success_test("Char Simple Letter A", "'A'", "char", 'A') - self.make_success_test("Char Simple Letter a", "'a'", "char", 'a') - self.make_success_test("Char Simple Letter Z", "'Z'", "char", 'Z') - self.make_success_test("Char Simple Letter z", "'z'", "char", 'z') + self.make_success_test("Char Simple Letter Uppercase A", "'A'", "char", 'A') + self.make_success_test("Char Simple Letter Lowercase a", "'a'", "char", 'a') + self.make_success_test("Char Simple Letter Uppercase Z", "'Z'", "char", 'Z') + self.make_success_test("Char Simple Letter Lowercase z", "'z'", "char", 'z') # Digits self.make_success_test("Char Digit 0", "'0'", "char", '0')