summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud.bailly@iohk.io>2024-09-25 12:12:53 +0200
committerArnaud Bailly <arnaud.bailly@iohk.io>2024-09-25 12:12:53 +0200
commitb849745636c17a9015198c2a1865b8dbb43ff4a3 (patch)
treecf3e8f34672a183f42dc8bf0c35a92afd1b3c381 /rust/src
parentec3637ab27df1fc9faeaac9977ee9e184cf4942e (diff)
downloadlambda-nantes-b849745636c17a9015198c2a1865b8dbb43ff4a3.tar.gz
Prepare parser to return multiple values
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/lambda.rs35
-rw-r--r--rust/src/main.rs8
-rw-r--r--rust/src/parser.rs29
3 files changed, 43 insertions, 29 deletions
diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs
index a6da87b..41c0c26 100644
--- a/rust/src/lambda.rs
+++ b/rust/src/lambda.rs
@@ -7,12 +7,15 @@ use ast::*;
mod parser;
use parser::*;
-pub fn run(arg: &str) -> String {
- let content = read_to_string(arg).unwrap();
- let value = parse(&content.to_string());
- let result = eval(&value);
-
- result.to_string()
+pub fn eval_file(file_name: &str) -> String {
+ let content = read_to_string(file_name).unwrap();
+ let values = parse(&content.to_string());
+ values
+ .iter()
+ .map(eval)
+ .map(|v| v.to_string())
+ .collect::<Vec<String>>()
+ .join(" ")
}
fn eval(arg: &Value) -> Value {
@@ -73,27 +76,31 @@ fn gensym() -> String {
mod lambda_test {
use crate::{eval, parse, Value};
+ fn parse1(string: &str) -> Value {
+ parse(string).pop().unwrap()
+ }
+
#[test]
fn evaluating_a_non_reducible_value_yields_itself() {
- let value = parse("(foo 12)");
+ let value = parse1("(foo 12)");
assert_eq!(value, eval(&value));
}
#[test]
fn evaluating_application_on_an_abstraction_reduces_it() {
- let value = parse("((lam x x) 12)");
+ let value = parse1("((lam x x) 12)");
assert_eq!(Value::Num(12), eval(&value));
}
#[test]
fn substitution_occurs_within_abstraction_body() {
- let value = parse("(((lam x (lam y x)) 13) 12)");
+ let value = parse1("(((lam x (lam y x)) 13) 12)");
assert_eq!(Value::Num(13), eval(&value));
}
#[test]
fn substitution_occurs_within_application_body() {
- let value = parse("(((lam x (lam y (y x))) 13) 12)");
+ let value = parse1("(((lam x (lam y (y x))) 13) 12)");
assert_eq!(
Value::App(Box::new(Value::Num(12)), Box::new(Value::Num(13))),
eval(&value)
@@ -102,26 +109,26 @@ mod lambda_test {
#[test]
fn substitution_does_not_capture_free_variables() {
- let value = parse("(((lam x (lam x x)) 13) 12)");
+ let value = parse1("(((lam x (lam x x)) 13) 12)");
assert_eq!(Value::Num(12), eval(&value));
}
#[test]
fn interpretation_applies_to_both_sides_of_application() {
- let value = parse("((lam x x) ((lam x x) 12))");
+ let value = parse1("((lam x x) ((lam x x) 12))");
assert_eq!(Value::Num(12), eval(&value));
}
#[test]
fn reduction_is_applied_until_normal_form_is_reached() {
- let value = parse("((((lam y (lam x (lam y (x y)))) 13) (lam x x)) 11)");
+ let value = parse1("((((lam y (lam x (lam y (x y)))) 13) (lam x x)) 11)");
assert_eq!(Value::Num(11), eval(&value));
}
#[test]
fn reduction_always_select_leftmost_outermost_redex() {
// this should not terminate if we evaluate the rightmost redex first
- let value = parse("((lam x 1) ((lam x (x x)) (lam x (x x))))");
+ let value = parse1("((lam x 1) ((lam x (x x)) (lam x (x x))))");
assert_eq!(Value::Num(1), eval(&value));
}
}
diff --git a/rust/src/main.rs b/rust/src/main.rs
index cf9477c..ffd92f2 100644
--- a/rust/src/main.rs
+++ b/rust/src/main.rs
@@ -1,7 +1,11 @@
use std::env::args;
-use lambda::run;
+use lambda::eval_file;
fn main() {
- println!("result: {}", run(&args().nth(1).unwrap()));
+ if args().count() > 1 {
+ for file in args().skip(1) {
+ println!("{}", eval_file(&file));
+ }
+ }
}
diff --git a/rust/src/parser.rs b/rust/src/parser.rs
index 9dc0bb8..c54386c 100644
--- a/rust/src/parser.rs
+++ b/rust/src/parser.rs
@@ -50,12 +50,15 @@ impl Parser {
}
}
-pub fn parse(arg: &str) -> Value {
+pub fn parse(arg: &str) -> Vec<Value> {
let tokens = tokenize(arg);
let mut parser = Parser { tokens, index: 0 };
- parse_expression(&mut parser)
+ let mut result = Vec::new();
+ let expr = parse_expression(&mut parser)
.map_err(|e| panic!("Syntax error: {}", e))
- .unwrap()
+ .unwrap();
+ result.push(expr);
+ result
}
fn parse_expression(parser: &mut Parser) -> Result<Value, String> {
@@ -168,26 +171,26 @@ mod tests {
#[test]
fn parse_integer_as_number(i in -1000i32..1000) {
let result = parse(&i.to_string());
- assert_eq!(Num(i), result);
+ assert_eq!(vec![Num(i)], result);
}
}
#[test]
fn parse_truth_values_as_booleans() {
- assert_eq!(Bool(true), parse("true"));
- assert_eq!(Bool(false), parse("false"));
+ assert_eq!(vec![Bool(true)], parse("true"));
+ assert_eq!(vec![Bool(false)], parse("false"));
}
#[test]
fn parse_identifiers_values_as_symbols() {
- assert_eq!(Sym("foo".to_string()), parse("foo"));
+ assert_eq!(vec![Sym("foo".to_string())], parse("foo"));
}
#[test]
fn ignores_whitespace() {
- assert_eq!(Sym("foo".to_string()), parse(" foo \n\r"));
- assert_eq!(Num(-42), parse("\n-42"));
+ assert_eq!(vec![Sym("foo".to_string())], parse(" foo \n\r"));
+ assert_eq!(vec![Num(-42)], parse("\n-42"));
}
#[test]
@@ -226,7 +229,7 @@ mod tests {
#[test]
fn parse_application_of_two_values() {
assert_eq!(
- App(Box::new(Sym("foo".to_string())), Box::new(Num(42))),
+ vec![App(Box::new(Sym("foo".to_string())), Box::new(Num(42)))],
parse("(foo 42)")
);
}
@@ -234,13 +237,13 @@ mod tests {
#[test]
fn parse_abstraction() {
assert_eq!(
- Lam(
+ vec![Lam(
"x".to_string(),
Box::new(App(
Box::new(Sym("x".to_string())),
Box::new(Sym("x".to_string()))
))
- ),
+ )],
parse("(lam x (x x))")
);
}
@@ -271,7 +274,7 @@ mod tests {
#[test]
fn parse_is_inverse_to_display(values in any::<Vec<Value>>()) {
let result : Vec<String> = values.iter().map(|v:&Value| v.to_string()).collect();
- assert_eq!(values, result.iter().map(|s| parse(s)).collect::<Vec<Value>>());
+ assert_eq!(values, result.iter().flat_map(|s| parse(s)).collect::<Vec<Value>>());
}
}
}