More compliance

Correct Math ops
Several equals funcs
Multiple bodies
trunk
Alona EM 2021-12-17 05:45:22 +00:00
parent 69b7714892
commit dadf839cca
5 changed files with 1799 additions and 271 deletions

View File

@ -3,13 +3,14 @@ use std::rc::Rc;
grammar<'s>();
pub(crate) Trees = Tree+;
pub(crate) Tree: Tree = {
"(" <Tree+> ")" => Tree::Branch(<>),
"(" "define" <Sym> <BTree> ")" => Tree::Define(<>),
"(" "define" "(" <name:Sym> <args:RcSlice<Sym>> ")" <body:Rc<Tree>> ")" => Tree::Define(name, Box::new(Tree::Lambda(Lambda(args, body)))),
"(" "define" "(" <name:Sym> <args:RcSlice<Sym>> ")" <body:RcSlice<Tree>> ")" => Tree::Define(name, Box::new(Tree::Lambda(Lambda(args, body)))),
"(" "if" <Tree> <Tree> <Tree> ")" => Tree::If(Box::new([<>])),
"(" "lambda (" <RcSlice<Sym>> ")" <Rc<Tree>> ")" => Tree::Lambda(Lambda(<>)),
"(" "lambda (" <RcSlice<Sym>> ")" <RcSlice<Tree>> ")" => Tree::Lambda(Lambda(<>)),
Literal => Tree::Leaf(<>),
}

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,14 @@
mod grammar;
use debug2::dbg;
// use debug2::dbg;
use rustyline::validate::{
MatchingBracketValidator, ValidationContext, ValidationResult, Validator,
};
use rustyline::Editor;
use rustyline_derive::{Completer, Helper, Highlighter, Hinter};
use std::collections::HashMap;
use std::io;
use std::rc::Rc;
#[derive(Debug, debug2::Debug, PartialEq)]
@ -23,7 +24,7 @@ enum Tree {
}
#[derive(Debug, debug2::Debug, PartialEq, Clone)]
struct Lambda(Rc<[String]>, Rc<Tree>);
struct Lambda(Rc<[String]>, Rc<[Tree]>);
#[derive(Debug, debug2::Debug, PartialEq)]
enum Literal {
@ -94,7 +95,7 @@ impl Value {
match self {
Self::Func(f) => Ok(Callable::Func(*f)),
Self::Lambda(l) => Ok(Callable::Lambda(l)),
_ => Err(RTError("Expected a function".to_owned())),
_ => Err(RTError(format!("Expected a function, got {:?}", self))),
}
}
@ -141,6 +142,25 @@ impl<'a> Env<'a> {
}
fn main() {
let args = std::env::args().collect::<Vec<_>>();
match &args[..] {
[_] => repl(),
[_, file] => run_file(file).unwrap(),
_ => panic!("To many args `{:?}`", args),
}
}
fn run_file(file: &str) -> io::Result<()> {
let src = std::fs::read_to_string(file)?;
let tree = grammar::TreesParser::new().parse(&src).unwrap();
let mut env = default_env();
for i in tree {
eval(&i, &mut env).unwrap();
}
Ok(())
}
fn repl() {
let mut rl = Editor::new();
rl.set_helper(Some(InputValidator {
brackets: MatchingBracketValidator::new(),
@ -182,11 +202,17 @@ fn eval(t: &Tree, env: &mut Env) -> Result<Value, RTError> {
Callable::Func(f) => f(&args)?,
Callable::Lambda(l) => {
if l.0.len() == args.len() {
let mut new_env = env.child();
let mut env = env.child();
for (x, y) in l.0.iter().zip(args) {
new_env.define(x.clone(), y)
env.define(x.clone(), y)
}
eval(&l.1, &mut new_env)?
let [main @ .., tail] = &l.1[..] else { unreachable!("Body has 1+ element by parser") };
for i in main {
eval(i, &mut env)?;
}
eval(tail, &mut env)?
} else {
return err(format!("Need {} args, got {}", l.0.len(), args.len()));
}
@ -214,7 +240,13 @@ fn default_env() -> Env<'static> {
("+", prims::add as Func),
("-", prims::sub as Func),
("/", prims::div as Func),
// TODO: Spec compiance
("=", prims::equals as Func),
("eq?", prims::equals as Func),
("eqv?", prims::equals as Func),
("equal?", prims::equals as Func),
("display", prims::display as Func),
("newline", prims::newline as Func),
] {
vars.insert(name.to_owned(), Value::Func(fun));
}
@ -226,8 +258,9 @@ fn default_env() -> Env<'static> {
}
mod prims {
use crate::{RTError, Value};
use crate::{err, RTError, Value};
// TODO: DRY +-/*
pub(crate) fn add(args: &[Value]) -> Result<Value, RTError> {
args.iter()
.map(Value::as_num)
@ -247,8 +280,13 @@ mod prims {
.get(0)
.ok_or_else(|| RTError("`div` needs at least one argument".to_owned()))?
.as_num()?;
let rest = mul(&args[1..])?.as_num().unwrap();
Ok(Value::Num(init / rest))
Ok(Value::Num(if args.len() == 1 {
1.0 / init
} else {
let rest = mul(&args[1..])?.as_num().unwrap();
init / rest
}))
}
pub(crate) fn sub(args: &[Value]) -> Result<Value, RTError> {
@ -256,13 +294,32 @@ mod prims {
.get(0)
.ok_or_else(|| RTError("`sub` needs at least one argument".to_owned()))?
.as_num()?;
let rest = add(&args[1..])?.as_num().unwrap();
Ok(Value::Num(init - rest))
Ok(Value::Num(if args.len() == 1 {
-init
} else {
let rest = add(&args[1..])?.as_num().unwrap();
init - rest
}))
}
pub(crate) fn equals(args: &[Value]) -> Result<Value, RTError> {
Ok(Value::Bool(args.array_windows().all(|[l, r]| l == r)))
}
pub(crate) fn display(args: &[Value]) -> Result<Value, RTError> {
let [arg] = args else {return err("To many args to `display`".to_owned())};
print!("{:?}", arg);
Ok(Value::NotAValue)
}
pub(crate) fn newline(args: &[Value]) -> Result<Value, RTError> {
if !args.is_empty() {
return err("Newline takes no args".to_owned());
}
println!("");
Ok(Value::NotAValue)
}
}
// #[cfg(test)]

28
test/pass/lambda-calc.scm Normal file
View File

@ -0,0 +1,28 @@
(define (displayln x) (display x) (newline))
(define (printbool x) (displayln (bool->int x)))
(define (true x y) x)
(define (false x y) y)
(define (and x y) (x y x))
(define (or x y) (x x y))
(define (not x) (x false true))
(define (bool->int x)
(if
(equal? x true) 1
(if (equal? x false) 0 (- 1))))
(printbool true)
(printbool false)
(printbool bool->int)
(printbool (not true))
(printbool (not false))
(printbool (and true true))
(printbool (and true false))
(printbool (and false true))
(printbool (and false false))
(printbool (or true true))
(printbool (or true false))
(printbool (or false true))
(printbool (or false false))
(printbool (and (or true false) (or false true)))

View File

@ -1 +1,16 @@
( + 1 2 ( / 2 3 4 5) )
(define (displayln x) (display x) (newline))
(displayln (+))
(displayln (+ 1))
(displayln (+ 1 2 3 4))
(displayln (+))
(displayln (- 1))
(displayln (- 1 2 3 4))
(displayln (* 1))
(displayln (* 1 2))
(displayln (*))
(displayln (/ 10))
(displayln (/ 10 5 2))