Skip to content
17 changes: 0 additions & 17 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,23 +318,6 @@ pub struct LambdaExpr {
pub body: LambdaBody,
}

/// Lambda expression body
#[derive(Debug, Clone)]
pub enum LambdaBody {
/// Expression body: `|x| -> x + 1`
Expr(Box<Spanned<Expr>>),
/// Block body: `|x| { give back x + 1; }`
Block(Vec<Statement>),
}

/// Lambda/closure expression: `|x, y| -> expr` or `|x, y| { ... }`
#[derive(Debug, Clone)]
pub struct LambdaExpr {
pub params: Vec<Parameter>,
pub return_type: Option<Type>,
pub body: LambdaBody,
}

/// Emote tag: `@name(params)`
#[derive(Debug, Clone)]
pub struct EmoteTag {
Expand Down
56 changes: 45 additions & 11 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,17 @@ pub enum RuntimeError {
#[error("Index out of bounds: {0}")]
IndexOutOfBounds(usize),

#[error("Negative index not allowed: {0}")]
NegativeIndex(i64),

#[error("Arity mismatch: expected {expected}, got {got}")]
ArityMismatch { expected: usize, got: usize },

#[error("Maximum recursion depth exceeded")]
RecursionLimitExceeded,

#[error("I/O error: {0}")]
IoError(String),
}

type Result<T> = std::result::Result<T, RuntimeError>;
Expand Down Expand Up @@ -92,6 +101,9 @@ impl Environment {
}

/// The WokeLang interpreter
/// Maximum recursion depth to prevent stack overflow
const MAX_RECURSION_DEPTH: usize = 1000;

pub struct Interpreter {
env: Environment,
functions: HashMap<String, FunctionDef>,
Expand All @@ -100,6 +112,7 @@ pub struct Interpreter {
consent_cache: HashMap<String, bool>,
verbose: bool,
care_mode: bool,
recursion_depth: usize,
}

impl Interpreter {
Expand All @@ -112,6 +125,7 @@ impl Interpreter {
consent_cache: HashMap::new(),
verbose: false,
care_mode: true,
recursion_depth: 0,
}
}

Expand Down Expand Up @@ -322,10 +336,14 @@ impl Interpreter {
} else {
// Ask user for consent
print!("Permission requested: '{}'. Allow? [y/N]: ", permission);
io::stdout().flush().unwrap();
io::stdout()
.flush()
.map_err(|e| RuntimeError::IoError(format!("Failed to flush stdout: {}", e)))?;

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
io::stdin()
.read_line(&mut input)
.map_err(|e| RuntimeError::IoError(format!("Failed to read input: {}", e)))?;
let granted = input.trim().eq_ignore_ascii_case("y");

self.consent_cache.insert(permission.clone(), granted);
Expand Down Expand Up @@ -566,7 +584,7 @@ impl Interpreter {
let idx = match index {
Value::Int(n) => {
if n < 0 {
return Err(RuntimeError::IndexOutOfBounds(n as usize));
return Err(RuntimeError::NegativeIndex(n));
}
n as usize
}
Expand All @@ -578,11 +596,13 @@ impl Interpreter {
.get(idx)
.cloned()
.ok_or(RuntimeError::IndexOutOfBounds(idx)),
Value::String(s) => s
.chars()
.nth(idx)
.map(|c| Value::String(c.to_string()))
.ok_or(RuntimeError::IndexOutOfBounds(idx)),
Value::String(s) => {
// Use chars().nth() for proper UTF-8 character indexing
s.chars()
.nth(idx)
.map(|c| Value::String(c.to_string()))
.ok_or(RuntimeError::IndexOutOfBounds(idx))
}
_ => Err(RuntimeError::TypeError(
"Cannot index this type".into(),
)),
Expand All @@ -609,7 +629,8 @@ impl Interpreter {
});
}
match &args[0] {
Value::String(s) => Ok(Some(Value::Int(s.len() as i64))),
// Use chars().count() for proper UTF-8 character count
Value::String(s) => Ok(Some(Value::Int(s.chars().count() as i64))),
Value::Array(a) => Ok(Some(Value::Int(a.len() as i64))),
_ => Err(RuntimeError::TypeError("len() requires string or array".into())),
}
Expand Down Expand Up @@ -690,10 +711,18 @@ impl Interpreter {
}

fn call_function(&mut self, name: &str, args: Vec<Value>) -> Result<Value> {
// Check recursion depth limit
if self.recursion_depth >= MAX_RECURSION_DEPTH {
return Err(RuntimeError::RecursionLimitExceeded);
}
self.recursion_depth += 1;

// First, check if name refers to a variable holding a closure
if let Some(value) = self.env.get(name).cloned() {
if let Value::Function(closure) = value {
return self.call_closure(&closure, args);
let result = self.call_closure(&closure, args);
self.recursion_depth -= 1;
return result;
}
}

Expand All @@ -702,9 +731,13 @@ impl Interpreter {
.functions
.get(name)
.cloned()
.ok_or_else(|| RuntimeError::UndefinedFunction(name.to_string()))?;
.ok_or_else(|| {
self.recursion_depth -= 1;
RuntimeError::UndefinedFunction(name.to_string())
})?;

if func.params.len() != args.len() {
self.recursion_depth -= 1;
return Err(RuntimeError::ArityMismatch {
expected: func.params.len(),
got: args.len(),
Expand Down Expand Up @@ -737,6 +770,7 @@ impl Interpreter {
}

self.env.pop_scope();
self.recursion_depth -= 1;

// Print goodbye message
if let Some(goodbye) = &func.goodbye {
Expand Down
13 changes: 13 additions & 0 deletions src/interpreter/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Value {
String(String),
Bool(bool),
Array(Vec<Value>),
/// Record/object/map with string keys
Record(HashMap<String, Value>),
Unit,
/// Result success: `Okay(value)`
Okay(Box<Value>),
Expand All @@ -69,6 +71,7 @@ impl Value {
Value::Float(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Array(a) => !a.is_empty(),
Value::Record(m) => !m.is_empty(),
Value::Unit => false,
Value::Okay(_) => true,
Value::Oops(_) => false,
Expand Down Expand Up @@ -113,6 +116,16 @@ impl fmt::Display for Value {
}
write!(f, "]")
}
Value::Record(fields) => {
write!(f, "{{")?;
for (i, (key, val)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", key, val)?;
}
write!(f, "}}")
}
Value::Unit => write!(f, "()"),
Value::Okay(v) => write!(f, "Okay({})", v),
Value::Oops(e) => write!(f, "Oops(\"{}\")", e),
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ pub mod interpreter;
pub mod lexer;
pub mod parser;
pub mod repl;
pub mod security;
pub mod stdlib;
pub mod typechecker;

pub use ast::Program;
pub use interpreter::Interpreter;
pub use lexer::Lexer;
pub use parser::Parser;
pub use repl::Repl;
pub use security::CapabilityRegistry;
pub use stdlib::StdlibRegistry;
pub use typechecker::TypeChecker;
5 changes: 3 additions & 2 deletions src/security/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ pub enum AuditAction {
pub struct CapabilityRegistry {
/// Granted capabilities
capabilities: HashMap<String, Vec<GrantedCapability>>,
/// Pending consent requests
/// Pending consent requests (reserved for async consent flows)
#[allow(dead_code)]
pending_requests: HashSet<Capability>,
/// Audit log
audit_log: Vec<AuditEntry>,
Expand Down Expand Up @@ -345,7 +346,7 @@ impl CapabilityRegistry {

/// Clear expired capabilities
pub fn cleanup_expired(&mut self) {
for (scope, caps) in self.capabilities.iter_mut() {
for (_scope, caps) in self.capabilities.iter_mut() {
caps.retain(|cap| {
if !cap.is_valid() {
// Log expiration if it was due to time
Expand Down
Loading
Loading