aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-06 14:15:12 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-06 14:15:12 +0300
commit23b2028bdce46d02209fc2df70fc5468a8beffa8 (patch)
tree31e7a6fae6d5d01759a84334555f4cd1fa26a038 /src
parent3d7ebeddc46e89c8e058b3f1805f836339a2f9ae (diff)
downloadmyslip-23b2028bdce46d02209fc2df70fc5468a8beffa8.tar.gz
myslip-23b2028bdce46d02209fc2df70fc5468a8beffa8.zip
Added boilerplate and tests for let-binds
Diffstat (limited to 'src')
-rw-r--r--src/parse/parsetree.rs1
-rw-r--r--src/sexp/display.rs1
-rw-r--r--src/sexp/mod.rs2
-rw-r--r--src/sexp/step.rs30
-rw-r--r--src/type/check.rs38
-rw-r--r--src/type/display.rs1
-rw-r--r--src/type/mod.rs4
-rw-r--r--src/type/subst.rs1
8 files changed, 78 insertions, 0 deletions
diff --git a/src/parse/parsetree.rs b/src/parse/parsetree.rs
index 13d73ea..75758dc 100644
--- a/src/parse/parsetree.rs
+++ b/src/parse/parsetree.rs
@@ -113,6 +113,7 @@ fn tokens_to_ast_inner(
Some(Sym(s)) if s == "false" => Ok(Atom(False)),
Some(Sym(s)) if s == "quote" => Ok(Atom(Quote)),
Some(Sym(s)) if s == "vector" => Ok(Atom(Vector)),
+ Some(Sym(s)) if s == "let" => Ok(Atom(Let)),
Some(Sym(s)) => Ok(Atom(Var(s))),
Some(ParClose) => break,
Some(ParOpen) => {
diff --git a/src/sexp/display.rs b/src/sexp/display.rs
index 57d1b50..c3db144 100644
--- a/src/sexp/display.rs
+++ b/src/sexp/display.rs
@@ -27,6 +27,7 @@ impl fmt::Display for SLeaf {
Var(s) => s.to_string(),
Quote => "quote".to_string(),
Vector => "vector".to_string(),
+ Let => "let".to_string(),
Nil => "()".to_string(),
})
}
diff --git a/src/sexp/mod.rs b/src/sexp/mod.rs
index 07d8373..4e3c11c 100644
--- a/src/sexp/mod.rs
+++ b/src/sexp/mod.rs
@@ -31,6 +31,8 @@ pub enum SLeaf {
Quote,
Vector,
+ Let,
+
Int(i32),
True,
False,
diff --git a/src/sexp/step.rs b/src/sexp/step.rs
index 535b183..e031fca 100644
--- a/src/sexp/step.rs
+++ b/src/sexp/step.rs
@@ -237,6 +237,36 @@ impl SExp {
/// scons(2, Nil))).step(),
/// Ok(scons(Vector, scons(1, scons(2, Nil))))
/// );
+ /// ```
+ ///
+ /// **Let-bindings**
+ /// ```rust
+ /// use myslip::sexp::{SExp::*, SLeaf::*, util::*};
+ ///
+ /// assert_eq!(
+ /// scons(
+ /// scons(Let, scons(var("x"), scons(5, Nil))), scons(
+ /// scons(Add, scons(var("x"), scons(1, Nil))), Nil
+ /// )
+ /// ).step(),
+ /// Ok(scons(Add, scons(5, scons(1, Nil))))
+ /// );
+ /// assert_eq!(
+ /// scons(
+ /// scons(Let, scons(var("y"), scons(4, Nil))),
+ /// scons(
+ /// scons(Let, scons(var("x"), scons(5, Nil))),
+ /// scons(scons(Add, scons(var("x"), scons(var("y"), Nil))), Nil)
+ /// )
+ /// ).step(),
+ /// Ok(scons(
+ /// scons(Let, scons(var("x"), scons(5, Nil))), scons(
+ /// scons(Add, scons(var("x"), scons(1, Nil))), Nil
+ /// )
+ /// ))
+ /// );
+ /// ```
+ ///
pub fn step(self) -> Result<Self, String> {
match self {
diff --git a/src/type/check.rs b/src/type/check.rs
index df3f4d9..d0ce807 100644
--- a/src/type/check.rs
+++ b/src/type/check.rs
@@ -81,6 +81,43 @@ impl SExp {
/// assert_eq!(Atom(Not).type_check(), Ok(arr(List(vec![Boolean]), Boolean)));
/// ```
///
+ ///
+ /// **Let-binding types**
+ ///
+ /// Let-bindings are quite interesting. Implementation is still a bit uncertain,
+ /// but at least we know that
+ /// ```rust
+ /// use myslip::{
+ /// r#type::{*, Type::*, TypeError::*, util::*},
+ /// sexp::{SExp::*, SLeaf::*, util::*},
+ /// };
+ ///
+ /// assert_eq!(
+ /// scons(
+ /// scons(Let, scons(var("x"), scons(False, Nil))),
+ /// scons(var("x"), Nil)
+ /// ).type_check(),
+ /// Ok(Boolean)
+ /// );
+ /// ```
+ ///
+ /// 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:
@@ -173,6 +210,7 @@ impl SExp {
vecof(vt("T")),
List(vec![VecType, vecof(vt("T"))])
)),
+ Atom(Let) => todo!(),
SCons(op, l) => {
diff --git a/src/type/display.rs b/src/type/display.rs
index 03f8f4b..fe54941 100644
--- a/src/type/display.rs
+++ b/src/type/display.rs
@@ -21,6 +21,7 @@ impl fmt::Display for Type {
Boolean => write!(f, "{}", "Bool"),
QuoteTy => write!(f, "{}", "Quote"),
VecType => write!(f, "{}", "Vector"),
+ LetType => write!(f, "{}", "Let"),
VecOf(ty) => write!(f, "({} ... {})", *ty, *ty),
Arrow(a, b) => write!(f, "({} -> {})", a, b),
List(types) => write!(
diff --git a/src/type/mod.rs b/src/type/mod.rs
index 0cbcf83..9e0d9ec 100644
--- a/src/type/mod.rs
+++ b/src/type/mod.rs
@@ -25,6 +25,8 @@ pub enum Type {
VecOf(Box<Type>),
VecType, // constructor
+ LetType,
+
/// Type for generics
/// and also error messages
/// with unknown types
@@ -102,6 +104,7 @@ impl Type {
Boolean => Ok(()),
QuoteTy => Ok(()),
VecType => Ok(()),
+ LetType => Ok(()),
Arrow(a, b) => b.is_concrete().and_then(|_ok| a.is_concrete()),
List(v) => {
let mut res = Ok(());
@@ -114,4 +117,5 @@ impl Type {
VarType(s) => Err(s.clone()),
}
}
+
}
diff --git a/src/type/subst.rs b/src/type/subst.rs
index 5f6573f..3ee4260 100644
--- a/src/type/subst.rs
+++ b/src/type/subst.rs
@@ -43,6 +43,7 @@ impl Type {
Boolean => Boolean,
QuoteTy => QuoteTy,
VecType => VecType,
+ LetType => LetType,
}
}