diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-06 16:23:18 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-06 16:23:18 +0300 |
commit | 313c044b4a878a425aaca6554576f5154ace8ff9 (patch) | |
tree | 706bf5e34678622111a23c7045f667c1acbe7c6d /src | |
parent | 23b2028bdce46d02209fc2df70fc5468a8beffa8 (diff) | |
download | myslip-313c044b4a878a425aaca6554576f5154ace8ff9.tar.gz myslip-313c044b4a878a425aaca6554576f5154ace8ff9.zip |
Implemented let-bindings
Diffstat (limited to 'src')
-rw-r--r-- | src/sexp/mod.rs | 12 | ||||
-rw-r--r-- | src/sexp/step.rs | 34 | ||||
-rw-r--r-- | src/sexp/subst.rs | 9 | ||||
-rw-r--r-- | src/sexp/util.rs | 10 | ||||
-rw-r--r-- | src/type/check.rs | 39 | ||||
-rw-r--r-- | src/type/display.rs | 7 | ||||
-rw-r--r-- | src/type/mod.rs | 2 |
7 files changed, 89 insertions, 24 deletions
diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs index 4e3c11c..7aa517b 100644 --- a/src/sexp/mod.rs +++ b/src/sexp/mod.rs @@ -59,11 +59,19 @@ use SLeaf::*; impl SExp { + /// Quick test that lets are values + /// ```rust + /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; + /// assert!(scons(Let, scons(var("a"), scons(5, Nil))).is_value()) + /// ``` pub fn is_value(&self) -> bool { match self { SCons(a, b) => - (**a == Atom(Quote) || **a == Atom(Vector)) - && b.consists_of_values(), + SCons(a.clone(), b.clone()).check_let().is_some() || + ( + (**a == Atom(Quote) || **a == Atom(Vector)) + && b.consists_of_values() + ), Atom(Var(_)) => false, Atom(_) => true, } diff --git a/src/sexp/step.rs b/src/sexp/step.rs index e031fca..816582c 100644 --- a/src/sexp/step.rs +++ b/src/sexp/step.rs @@ -240,6 +240,8 @@ impl SExp { /// ``` /// /// **Let-bindings** + /// + /// Basic operation: /// ```rust /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; /// @@ -261,7 +263,27 @@ impl SExp { /// ).step(), /// Ok(scons( /// scons(Let, scons(var("x"), scons(5, Nil))), scons( - /// scons(Add, scons(var("x"), scons(1, Nil))), Nil + /// scons(Add, scons(var("x"), scons(4, Nil))), Nil + /// ) + /// )) + /// ); + /// ``` + /// + /// Shadowing: + /// ```rust + /// use myslip::sexp::{SExp::*, SLeaf::*, util::*}; + /// + /// assert_eq!( + /// scons( + /// scons(Let, scons(var("x"), scons(4, Nil))), + /// scons( + /// scons(Let, scons(var("x"), scons(5, Nil))), + /// scons(scons(Add, scons(var("x"), scons(4, Nil))), Nil) + /// ) + /// ).step(), + /// Ok(scons( + /// scons(Let, scons(var("x"), scons(5, Nil))), scons( + /// scons(Add, scons(var("x"), scons(4, Nil))), Nil /// ) /// )) /// ); @@ -278,6 +300,16 @@ impl SExp { SCons(op, l) if !(*op).is_value() => Ok(scons(op.step()?, l)), + // let binds + SCons(op, l) if op.clone().check_let().is_some() => match (*op).check_let() { + Some((varname, val)) => Ok(match *l { + SCons(a, b) if *b == Atom(Nil) => *a, + t => t, + }.subst(&varname, &val)), + None => panic!("unreachable as per match guard arm"), + }, + + // op value and a1 .. an values, and b0, b1 .. bn not values // --------------------------------------------------------- // (op a1 ... an b) -> (op a1 ... an b0.step() b1 .. bn) diff --git a/src/sexp/subst.rs b/src/sexp/subst.rs index 87f9b50..4cfa48d 100644 --- a/src/sexp/subst.rs +++ b/src/sexp/subst.rs @@ -17,7 +17,14 @@ impl SExp { /// ``` pub fn subst(self, name: &str, value: &SExp) -> SExp { match self { - SCons(a, b) => scons((*a).subst(name, value), (*b).subst(name, value)), + SCons(a, b) => { + if let Some((varname, _)) = a.clone().check_let() { + if &varname == name { + return SCons(a, b); + } + } + scons((*a).subst(name, value), (*b).subst(name, value)) + }, Atom(Var(x)) if x == name => value.clone(), t => t, } diff --git a/src/sexp/util.rs b/src/sexp/util.rs index b3edf19..a28aaad 100644 --- a/src/sexp/util.rs +++ b/src/sexp/util.rs @@ -76,4 +76,14 @@ impl SExp { }, } } + + pub fn check_let(self) -> Option<(String, SExp)> { + match &(self.parts())[..] { + [l, n, v] if *l == Atom(Let) => match (n.clone(), v.clone()) { + (Atom(Var(name)), val) => Some((name, val)), + _ => None + }, + _ => None + } + } } diff --git a/src/type/check.rs b/src/type/check.rs index d0ce807..e536ce7 100644 --- a/src/type/check.rs +++ b/src/type/check.rs @@ -1,6 +1,6 @@ use crate::r#type::{Type, TypeError, Type::*, TypeError::*, util::*}; -use crate::sexp::{SExp, SExp::*, SLeaf::*}; +use crate::sexp::{SExp, SExp::*, SLeaf::*, util::*}; use std::collections::HashMap; impl SExp { @@ -101,23 +101,6 @@ impl SExp { /// ); /// ``` /// - /// As such it must be that the type of the let-binding s-expression is st. - /// ```rust - /// use myslip::{ - /// r#type::{*, Type::*, TypeError::*, util::*}, - /// sexp::{SExp::*, SLeaf::*, util::*}, - /// }; - /// - /// assert_eq!( - /// scons(Let, scons(var("x"), scons(1, Nil))).type_check(), - /// Ok(arr(vt("T"), vt("T"))) - /// ); - /// ``` - /// ie. the identity function. - /// - /// No more behavior on typing let can be defined, as typing the let-keyword - /// would require the yet untyped variable to be typed. - /// /// Though perhaps the most important task of the type system /// is to increase safety by being able to warn about errors /// before evaluation. Here are some failing examples: @@ -184,7 +167,7 @@ impl SExp { } - fn infer_type(&self, ctx: HashMap<String, Type>) -> Result<Type, TypeError> { + fn infer_type(&self, mut ctx: HashMap<String, Type>) -> Result<Type, TypeError> { match self { @@ -210,13 +193,29 @@ impl SExp { vecof(vt("T")), List(vec![VecType, vecof(vt("T"))]) )), - Atom(Let) => todo!(), + Atom(Let) => Ok(LetType), SCons(op, l) => { + + // Let-expressions + if let Some((varname, val)) = (*op).clone().check_let() { + let valtype = val.infer_type(ctx.clone())?; + ctx.insert(varname, valtype); + return match (**l).clone() { + SCons(exp, nil) if *nil == Atom(Nil) => *exp, + t => t + }.infer_type(ctx); + } + if **op == Atom(Let) { + return Err(LetAsOperator(scons(op.clone(), l.clone()))); + } + + // Normal operation let opertype = (*op).infer_type(ctx.clone())?; let argstype = (*l).infer_list_type(ctx)?; + let argstype = if opertype == arr( vecof(vt("T")), List(vec![VecType, vecof(vt("T"))]) diff --git a/src/type/display.rs b/src/type/display.rs index fe54941..4a9b54a 100644 --- a/src/type/display.rs +++ b/src/type/display.rs @@ -103,6 +103,13 @@ impl fmt::Display for TypeError { argtype, generictype ) }, + LetAsOperator(letexp) => { + write!( + f, + "let used as operator instead of generating an operator: maybe try '({} ...)' instead", + letexp + ) + }, OtherError => write!(f, "uncategorized error"), } } diff --git a/src/type/mod.rs b/src/type/mod.rs index 9e0d9ec..afb5a0b 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -59,6 +59,8 @@ pub enum TypeError { generictype: Type, }, + LetAsOperator(SExp), + OtherError } |