Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions compiler/v1/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,25 @@ pub enum Expr {
callee: Box<Expr>,
args: Vec<Expr>,
},
ArrayLiteral {
elements: Vec<Expr>,
},
DictLiteral {
entries: Vec<(String, Expr)>,
},
Index {
target: Box<Expr>,
index: Box<Expr>,
},
Get {
object: Box<Expr>,
name: String,
},
MethodCall {
receiver: Box<Expr>,
name: String,
args: Vec<Expr>,
},
Unary {
op: TokenKind,
right: Box<Expr>,
Expand Down
112 changes: 112 additions & 0 deletions compiler/v1/src/interpreter/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use super::control_flow::ControlFlow;
use super::environment::Environment;
use super::function::Function;
use super::value::Value;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;

pub struct Executor;

Expand Down Expand Up @@ -121,6 +124,66 @@ impl Executor {
let right_val = self.evaluate_expr(right, env)?;
self.evaluate_binary(left_val, op, right_val)
}
Expr::ArrayLiteral { elements } => {
let mut evaluated = Vec::new();
for el in elements {
evaluated.push(self.evaluate_expr(el, env)?);
}
Ok(Value::Array(Rc::new(RefCell::new(evaluated))))
}
Expr::DictLiteral { entries } => {
let mut m = HashMap::new();
for (k, v) in entries {
let value = self.evaluate_expr(v, env)?;
m.insert(k.clone(), value);
}
Ok(Value::Dict(Rc::new(RefCell::new(m))))
}
Expr::Index { target, index } => {
let target_val = self.evaluate_expr(target, env)?;
let index_val = self.evaluate_expr(index, env)?;
match (target_val, index_val) {
(Value::Array(arr), Value::Number(n)) => {
if n.fract() != 0.0 {
return Err("Array index must be an integer".to_string());
}
let idx = n as isize;
if idx < 0 {
return Err("Array index must be non-negative".to_string());
}
let idx = idx as usize;
arr.borrow()
.get(idx)
.cloned()
.ok_or_else(|| "Array index out of bounds".to_string())
}
(Value::Dict(d), Value::String(s)) => d
.borrow()
.get(&s)
.cloned()
.ok_or_else(|| "Dictionary key not found".to_string()),
_ => Err("Indexing is only supported for arrays (number index) and dictionaries (string key)".to_string()),
}
}
Expr::Get { object, name } => {
let obj = self.evaluate_expr(object, env)?;
match obj {
Value::Dict(d) => d
.borrow()
.get(name)
.cloned()
.ok_or_else(|| "Dictionary key not found".to_string()),
_ => Err("Property access is only supported for dictionaries".to_string()),
}
}
Expr::MethodCall { receiver, name, args } => {
let recv = self.evaluate_expr(receiver, env)?;
let mut evaluated_args = Vec::new();
for a in args {
evaluated_args.push(self.evaluate_expr(a, env)?);
}
self.evaluate_method_call(recv, name, &evaluated_args)
}
Expr::Unary { op, right } => {
let right_val = self.evaluate_expr(right, env)?;
self.evaluate_unary(op, right_val)
Expand Down Expand Up @@ -206,6 +269,55 @@ impl Executor {
}
}

fn evaluate_method_call(&self, receiver: Value, name: &str, args: &[Value]) -> Result<Value, String> {
match (receiver, name) {
(Value::String(s), "length") => {
if !args.is_empty() {
return Err(format!("{}.length() expects 0 arguments, got {}", "String", args.len()));
}
Ok(Value::Number(s.chars().count() as f64))
}
(Value::String(s), "upper") => {
if !args.is_empty() {
return Err(format!("{}.upper() expects 0 arguments, got {}", "String", args.len()));
}
Ok(Value::String(s.to_uppercase()))
}
(Value::String(s), "lower") => {
if !args.is_empty() {
return Err(format!("{}.lower() expects 0 arguments, got {}", "String", args.len()));
}
Ok(Value::String(s.to_lowercase()))
}
(Value::String(s), "contains") => {
if args.len() != 1 {
return Err(format!("{}.contains() expects 1 argument, got {}", "String", args.len()));
}
match &args[0] {
Value::String(needle) => Ok(Value::Bool(s.contains(needle))),
_ => Err("String.contains() expects a string argument".to_string()),
}
}
(Value::String(s), "split") => {
if args.len() != 1 {
return Err(format!("{}.split() expects 1 argument, got {}", "String", args.len()));
}
let delim = match &args[0] {
Value::String(d) => d.clone(),
_ => return Err("String.split() expects a string delimiter".to_string()),
};
let parts = if delim.is_empty() {
s.chars().map(|c| c.to_string()).collect::<Vec<_>>()
} else {
s.split(&delim).map(|p| p.to_string()).collect::<Vec<_>>()
};
let arr = parts.into_iter().map(Value::String).collect::<Vec<_>>();
Ok(Value::Array(Rc::new(RefCell::new(arr))))
}
(other, _) => Err(format!("Method call not supported on {:?}", other)),
}
}

fn isTruthy(&self, value: &Value) -> bool {
match value {
Value::Bool(false) => false,
Expand Down
17 changes: 15 additions & 2 deletions compiler/v1/src/interpreter/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ast::Stmt;
use crate::ast::{Expr, Stmt};
use crate::typecheck::TypeChecker;
use super::environment::Environment;
use super::executor::Executor;
use super::std::StdLib;
Expand All @@ -24,7 +25,19 @@ impl Interpreter {
}

pub fn interpret(&mut self, statements: &[Stmt]) -> Result<(), String> {
let mut checker = TypeChecker::new();
checker.checkProgram(statements)?;

self.executor.execute_block(statements, &mut self.environment)?;

if self.environment.get("main").is_some() {
let call = Expr::Call {
callee: Box::new(Expr::Variable("main".to_string())),
args: vec![],
};
self.executor.evaluate_expr(&call, &mut self.environment)?;
}

Ok(())
}
}
}
110 changes: 102 additions & 8 deletions compiler/v1/src/interpreter/std.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::value::Value;
use super::function::Function;
use crate::lexer::span::Span;
use std::io::{self, Write};

pub struct StdLib;

Expand All @@ -27,18 +28,111 @@ impl StdLib {
if i > 0 {
print!(" ");
}
match arg {
Value::String(s) => print!("{}", s),
Value::Number(n) => print!("{}", n),
Value::Bool(b) => print!("{}", b),
Value::Null => print!("null"),
Value::Function(_) => print!("<function>"),
}
print!("{}", Self::formatValue(arg));
}
println!();
Some(Ok(Value::Null))
}
"len" => {
if args.len() != 1 {
return Some(Err(format!("len expects 1 argument, got {}", args.len())));
}
match &args[0] {
Value::String(s) => Some(Ok(Value::Number(s.chars().count() as f64))),
Value::Array(arr) => Some(Ok(Value::Number(arr.borrow().len() as f64))),
Value::Dict(d) => Some(Ok(Value::Number(d.borrow().len() as f64))),
_ => Some(Err("len expects a string, array, or dictionary".to_string())),
}
}
"push" => {
if args.len() != 2 {
return Some(Err(format!("push expects 2 arguments, got {}", args.len())));
}
match &args[0] {
Value::Array(arr) => {
arr.borrow_mut().push(args[1].clone());
Some(Ok(Value::Null))
}
_ => Some(Err("push expects an array as first argument".to_string())),
}
}
"pop" => {
if args.len() != 1 {
return Some(Err(format!("pop expects 1 argument, got {}", args.len())));
}
match &args[0] {
Value::Array(arr) => {
let v = arr.borrow_mut().pop().unwrap_or(Value::Null);
Some(Ok(v))
}
_ => Some(Err("pop expects an array".to_string())),
}
}
"input" => {
if args.len() > 1 {
return Some(Err(format!("input expects 0 or 1 arguments, got {}", args.len())));
}
if args.len() == 1 {
match &args[0] {
Value::String(s) => {
print!("{}", s);
let _ = io::stdout().flush();
}
_ => return Some(Err("input prompt must be a string".to_string())),
}
}

let mut line = String::new();
match io::stdin().read_line(&mut line) {
Ok(_) => {
while line.ends_with('\n') || line.ends_with('\r') {
line.pop();
}
Some(Ok(Value::String(line)))
}
Err(e) => Some(Err(format!("failed to read input: {}", e))),
}
}
_ => None, // Not a built-in function
}
}
}

fn formatValue(value: &Value) -> String {
match value {
Value::String(s) => s.clone(),
Value::Number(n) => {
if n.fract() == 0.0 {
format!("{}", *n as i64)
} else {
format!("{}", n)
}
}
Value::Bool(b) => format!("{}", b),
Value::Null => "null".to_string(),
Value::Function(_) => "<function>".to_string(),
Value::Array(arr) => {
let items = arr
.borrow()
.iter()
.map(Self::formatValue)
.collect::<Vec<_>>()
.join(", ");
format!("[{}]", items)
}
Value::Dict(d) => {
let d = d.borrow();
let mut keys = d.keys().cloned().collect::<Vec<_>>();
keys.sort();
let items = keys
.into_iter()
.map(|k| {
let v = d.get(&k).expect("key came from map");
format!("{}: {}", k, Self::formatValue(v))
})
.collect::<Vec<_>>()
.join(", ");
format!("{{{}}}", items)
}
}
}
}
41 changes: 39 additions & 2 deletions compiler/v1/src/interpreter/value.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
use crate::ast::Literal;

use super::function::Function;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub enum Value {
String(String),
Number(f64),
Bool(bool),
Function(Function),
Array(Rc<RefCell<Vec<Value>>>),
Dict(Rc<RefCell<HashMap<String, Value>>>),
Null,
}

impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::String(a), Value::String(b)) => a == b,
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b,
(Value::Array(a), Value::Array(b)) => {
let a = a.borrow();
let b = b.borrow();
a.as_slice() == b.as_slice()
}
(Value::Dict(a), Value::Dict(b)) => {
let a = a.borrow();
let b = b.borrow();
if a.len() != b.len() {
return false;
}
for (k, av) in a.iter() {
match b.get(k) {
Some(bv) if av == bv => {}
_ => return false,
}
}
true
}
(Value::Null, Value::Null) => true,
_ => false,
}
}
}

impl From<Literal> for Value {
fn from(lit: Literal) -> Self {
match lit {
Expand All @@ -19,4 +56,4 @@ impl From<Literal> for Value {
Literal::Bool(b) => Value::Bool(b),
Literal::Null => Value::Null, }
}
}
}
2 changes: 2 additions & 0 deletions compiler/v1/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ impl<'a> Lexer<'a> {
')' => Ok(self.simpleToken(TokenKind::RightParen, start)),
'{' => Ok(self.simpleToken(TokenKind::LeftBrace, start)),
'}' => Ok(self.simpleToken(TokenKind::RightBrace, start)),
'[' => Ok(self.simpleToken(TokenKind::LeftBracket, start)),
']' => Ok(self.simpleToken(TokenKind::RightBracket, start)),
';' => Ok(self.simpleToken(TokenKind::Semicolon, start)),
'+' => Ok(self.simpleToken(TokenKind::Plus, start)),
'-' => Ok(self.simpleToken(TokenKind::Minus, start)),
Expand Down
2 changes: 2 additions & 0 deletions compiler/v1/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum TokenKind {
RightParen,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Comma,
Dot,
Minus,
Expand Down
Loading
Loading