Skip to content
Closed
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
17 changes: 16 additions & 1 deletion src/environment/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ pub struct Environment<A> {

impl<A: Clone> Environment<A> {
pub fn new() -> Environment<A> {
Environment {
let mut env = Environment {
globals: Scope::new(),
stack: LinkedList::new(),
};
for (_name, func) in crate::stdlib::builtins() {
env.map_function(func);
}
env
}

pub fn map_variable(&mut self, var: Name, mutable: bool, value: A) -> () {
Expand Down Expand Up @@ -194,13 +198,15 @@ mod tests {
kind: Type::TVoid,
params: Vec::new(),
body: None,
builtin: None,
};

let local_func = Function {
name: "local".to_string(),
kind: Type::TVoid,
params: Vec::new(),
body: None,
builtin: None,
};

// Test function scoping
Expand All @@ -217,4 +223,13 @@ mod tests {
assert!(env.lookup_function(&"global".to_string()).is_some()); // global still visible
assert!(env.lookup_function(&"local".to_string()).is_none()); // local gone
}

#[test]
fn test_input_builtin_registration() {
let mut env: Environment<i32> = Environment::new();
for (_name, func) in crate::stdlib::builtins() {
env.map_function(func);
}
assert!(env.lookup_function(&"input".to_string()).is_some());
}
}
63 changes: 32 additions & 31 deletions src/interpreter/expression_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,39 +389,40 @@ pub fn eval_function_call(
) -> Result<ExpressionResult, String> {
match env.lookup_function(&name) {
Some(function_definition) => {
let mut new_env = Environment::new();

if args.len() != function_definition.params.len() {
return Err(format!(
"[Runtime Error] Invalid number of arguments for '{}'.",
name
));
}

new_env.push();

for (formal, actual) in function_definition.params.iter().zip(args.iter()) {
let value = match eval(actual.clone(), env)? {
ExpressionResult::Value(expr) => expr,
ExpressionResult::Propagate(expr) => {
return Ok(ExpressionResult::Propagate(expr))
}
};
new_env.map_variable(formal.argument_name.clone(), false, value);
}

// Execute the body of the function.
match super::statement_execute::execute(
*function_definition.body.as_ref().unwrap().clone(),
&new_env,
) {
Ok(Computation::Continue(_)) => Err("Function did not return a value".to_string()),
Ok(Computation::Return(value, _)) => Ok(ExpressionResult::Value(value)),
Ok(Computation::PropagateError(value, _)) => Ok(ExpressionResult::Propagate(value)),
Err(e) => Err(e),
if let Some(callback) = function_definition.builtin {
return callback(&args);
} else if let Some(body) = &function_definition.body {
let mut new_env = Environment::new();
if args.len() != function_definition.params.len() {
return Err(format!(
"[Runtime Error] Invalid number of arguments for '{}'.",
name
));
}
new_env.push();
for (formal, actual) in function_definition.params.iter().zip(args.iter()) {
let value = match eval(actual.clone(), env)? {
ExpressionResult::Value(expr) => expr,
ExpressionResult::Propagate(expr) => {
return Ok(ExpressionResult::Propagate(expr))
}
};
new_env.map_variable(formal.argument_name.clone(), false, value);
}
match super::statement_execute::execute(
*body.clone(),
&new_env,
) {
Ok(Computation::Continue(_)) => Err("Function did not return a value".to_string()),
Ok(Computation::Return(value, _)) => Ok(ExpressionResult::Value(value)),
Ok(Computation::PropagateError(value, _)) => Ok(ExpressionResult::Propagate(value)),
Err(e) => Err(e),
}
} else {
return Err("Função sem implementação".to_string());
}
}
_ => Err(format!("Function {} not found", name)),
None => Err(format!("Function {} not found", name)),
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/ir/ast.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::interpreter::expression_eval::ExpressionResult;
// Type alias for variable and function names
pub type Name = String;

Expand All @@ -8,6 +9,7 @@ pub struct Function {
pub kind: Type,
pub params: Vec<FormalArgument>,
pub body: Option<Box<Statement>>,
pub builtin: Option<BuiltinCallback>,
}

impl Function {
Expand All @@ -18,6 +20,7 @@ impl Function {
kind: Type::TVoid,
params: Vec::new(),
body: None,
builtin: None,
};
}
}
Expand Down Expand Up @@ -146,3 +149,5 @@ pub enum Statement {
Return(Box<Expression>),
TypeDeclaration(Name, Vec<ValueConstructor>),
}

pub type BuiltinCallback = fn(&[Expression]) -> Result<ExpressionResult, String>;
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
pub mod ir;
pub mod parser;
pub mod interpreter;
pub mod environment;
pub mod stdlib;
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod interpreter;
pub mod ir;
pub mod parser;
pub mod type_checker;
mod stdlib;

fn main() {
println!("Hello, world!");
Expand Down
2 changes: 2 additions & 0 deletions src/parser/parser_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement>
kind: t,
params: args,
body: Some(Box::new(block)),
builtin: None,
})
},
)(input)
Expand Down Expand Up @@ -301,6 +302,7 @@ mod tests {
"x".to_string(),
Box::new(Expression::CInt(1)),
)]))),
builtin: None,
});
let parsed = parse_function_definition_statement(input).unwrap().1;
assert_eq!(parsed, expected);
Expand Down
45 changes: 45 additions & 0 deletions src/stdlib/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::ir::ast::{Expression, Function, Type, FormalArgument};
use crate::interpreter::expression_eval::ExpressionResult;
use std::collections::HashMap;
use std::io::{self, Write};

pub fn register_builtins(map: &mut HashMap<String, Function>) {
map.insert("input".to_string(), Function {
name: "input".to_string(),
kind: Type::TFunction(Box::new(Some(Type::TString)), vec![Type::TMaybe(Box::new(Type::TString))]),
params: vec![FormalArgument::new("prompt".to_string(), Type::TMaybe(Box::new(Type::TString)))],
body: None,
builtin: Some(builtin_input),
});
map.insert("print".to_string(), Function {
name: "print".to_string(),
kind: Type::TFunction(Box::new(Some(Type::TVoid)), vec![Type::TAny]),
params: vec![FormalArgument::new("value".to_string(), Type::TAny)],
body: None,
builtin: Some(builtin_print),
});
}

pub fn builtin_input(args: &[Expression]) -> Result<ExpressionResult, String> {
let prompt = if let Some(Expression::CString(msg)) = args.get(0) {
msg.as_str()
} else {
""
};
print!("{}", prompt);
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).map_err(|e| e.to_string())?;
let input = input.trim_end_matches(['\n', '\r']).to_string();
Ok(ExpressionResult::Value(Expression::CString(input)))
}

pub fn builtin_print(args: &[Expression]) -> Result<ExpressionResult, String> {
let message = args.get(0).cloned().unwrap_or(Expression::CString("".to_string()));
if let Expression::CString(s) = &message {
println!("{}", s);
} else {
println!("{:?}", message);
}
Ok(ExpressionResult::Value(Expression::CVoid))
}
10 changes: 10 additions & 0 deletions src/stdlib/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod io;

use std::collections::HashMap;
use crate::ir::ast::Function;

pub fn builtins() -> HashMap<String, Function> {
let mut map = HashMap::new();
io::register_builtins(&mut map);
map
}
3 changes: 3 additions & 0 deletions src/type_checker/statement_type_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ mod tests {
Box::new(Var("a".to_string())),
Box::new(Var("b".to_string())),
))))),
builtin: None,
});
match check_stmt(func, &env) {
Ok(_) => assert!(true),
Expand Down Expand Up @@ -508,13 +509,15 @@ mod tests {
kind: Type::TVoid,
params: Vec::new(),
body: None,
builtin: None,
};

let _local_func = Function {
name: "local".to_string(),
kind: Type::TVoid,
params: Vec::new(),
body: None,
builtin: None,
};

// Test function scoping
Expand Down
1 change: 1 addition & 0 deletions tests/parser_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ mod statement_tests {
Box::new(Expression::Var("y".to_string())),
)),
)]))),
builtin: None,
});

let (rest, result) = parse_statement(input).unwrap();
Expand Down