aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md63
-rw-r--r--TUTORIAL.md84
-rw-r--r--src/main.rs63
-rw-r--r--src/parse/parsetree.rs15
-rw-r--r--src/sexp/display.rs25
-rw-r--r--src/sexp/step.rs8
-rw-r--r--src/type/check.rs17
-rw-r--r--src/type/display.rs20
-rw-r--r--src/type/mod.rs5
9 files changed, 260 insertions, 40 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..55a1c9a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+
+
+myslip README
+=============
+
+Below is a short introduction to this programming language
+and instructions on setting it up.
+`TUTORIAL.md` may help getting familiar with the language.
+
+
+Language introduction
+---------------------
+STILL TODO (or to modify)
+how functional is it?
+myslip is a lisp-inspired language, but a bit different
+(mostly, because I have never really written lisp except
+ for in emacs, so I have no clue of what lisp usually is
+ like).
+Valid myslip s-expressions may be atoms of different types
+or lists of them, though not all syntactically valid
+myslip expressions are ones that can be evaluated.
+
+S-expression := Atom | List
+Atom := TODO
+List := (S-expression ... S-expression)
+
+The myslip interpreter performs type checking on
+s-expressions and evaluates them according to polish
+notation, ie. the first S-expression of a list is taken to
+be the operator that is passed the rest of the expressions
+as arguments.
+
+
+Running the project
+-------------------
+
+myslip can be built using cargo 1.88.0. Instructions to
+install cargo can be found online:
+https://doc.rust-lang.org/cargo/getting-started/installation.html
+
+If you are having trouble due to having a different version
+and you used rustup for your cargo installation, you can
+follow these instructions on how to change the rust version
+https://rust-lang.github.io/rustup/overrides.html
+
+TODO: system requirements? try it out on debian
+```console
+TODO
+$ sudo apt install curl git
+$ curl ??? | sh ???
+$ git clone ???
+$ cd myslip
+```
+and then the interpreter can be accessed either via
+`cargo run`, or then you can add it to your path:
+```console
+TODO
+$ cargo build
+$ ln ??? ???
+$ myslip
+```
+
+
diff --git a/TUTORIAL.md b/TUTORIAL.md
new file mode 100644
index 0000000..7389d55
--- /dev/null
+++ b/TUTORIAL.md
@@ -0,0 +1,84 @@
+
+
+Tutorial
+========
+
+This tutorial consists of a language tour
+and a set of exercises, in the order of
+mentioning.
+
+
+Tour
+----
+
+
+**Arithmetic operations**
+
+Addition, multiplication, subtraction and division
+are supported with +, *, - and / respectively.
+```console
+> (+ 1 1)
+2 : Int
+> (* 2 3)
+6 : Int
+> (- 5 4)
+1 : Int
+> (/ 4 2)
+2 : Int
+```
+
+Decimals are truncated in division:
+```console
+> (/ 5 2)
+2 : Int
+```
+
+
+**Lists**
+
+Lists in myslip correspond to what is known as tuples in many
+other programming languages. This difference exists because
+myslip is a list processor, and not using this underlying
+construct of the language would be odd.
+
+In principle,
+```myslip
+(1 2 3 4 5)
+```
+is a valid list, but it is evaluated by the interpreter,
+which assumes that the first term, `1`, is an operator.
+That's why constructing a list requires the operator
+`quote`:
+```console
+> quote
+quote : (T -> T)
+> (quote 1 2 3 4 5)
+(quote 1 2 3 4 5) : (Int Int Int Int Int)
+```
+
+As hinted above, `quote` is typewise the identity function.
+Behaviorally it does though simplify sub-expressions.
+```console
+> (quote (+ 1 1) (+ 2 2))
+(quote 2 4) : (Int Int)
+```
+
+The elements of a list can of course be of different types:
+```console
+> (quote + 0 (quote 1 2))
+(quote + 0 (quote 1 2)) : (((Int Int) -> Int) Int (Int Int))
+```
+
+TODO: List destructuring
+
+
+**Understanding error messages**
+TODO: div zero
+TODO: unclosed parenthesis
+TODO: type errors
+
+
+Exercises
+---------
+
+TODO
diff --git a/src/main.rs b/src/main.rs
index e7a11a9..80a4066 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,64 @@
+
+use melisp::parse::parsetree::parse_to_ast;
+use melisp::sexp::{SExp::*, SLeaf::Nil};
+use std::{io, io::Write};
+
fn main() {
- println!("Hello, world!");
+ match repl() {
+ Ok(()) => println!("bye :)"),
+ Err(e) => {
+ println!("Error: {}", e);
+ main();
+ },
+ }
}
+
+fn repl() -> Result<(), String> {
+
+ let stdin = io::stdin();
+
+ let mut input = String::new();
+
+ let mut stdout = io::stdout();
+
+ print!("> ");
+ match stdout.flush() {
+ Ok(_) => (),
+ Err(_) => println!("Enter s-expression:"),
+ };
+ let mut success = stdin.read_line(&mut input);
+
+ while success.is_ok() && input != "exit\n" {
+ if let Ok(0) = success {
+ print!("\n");
+ break;
+ }
+
+ let expression = match parse_to_ast(&input) {
+ Ok(SCons(a, b)) if *b == Atom(Nil) => Ok(*a),
+ t => t
+ }?;
+
+ let ty = expression.type_check().map_err(|e| e.to_string())?;
+
+ let result = expression.multistep()?;
+
+ println!("{} : {}", result, ty);
+
+ print!("> ");
+ match stdout.flush() {
+ Ok(_) => (),
+ Err(_) => println!("Enter s-expression:"),
+ };
+ input.clear();
+ success = stdin.read_line(&mut input);
+
+ }
+
+ match success {
+ Ok(_) => Ok(()),
+ Err(e) => Err(format!("read error: {e}")),
+ }
+
+}
+
diff --git a/src/parse/parsetree.rs b/src/parse/parsetree.rs
index 7e1c80d..5507b0b 100644
--- a/src/parse/parsetree.rs
+++ b/src/parse/parsetree.rs
@@ -21,19 +21,6 @@ pub enum Token {
Whitespace(String),
}
-impl Token {
- fn same_variant(&self, other: Token) -> bool {
- match (self, other) {
- (ParOpen, ParOpen) => true,
- (ParClose, ParClose) => true,
- (Num(_), Num(_)) => true,
- (Sym(_), Sym(_)) => true,
- (Whitespace(_), Whitespace(_)) => true,
- _ => false
- }
- }
-}
-
use std::fmt;
use std::fmt::Display;
impl Display for Token {
@@ -88,8 +75,6 @@ fn tokens_to_ast_inner(
mut input: VecDeque<Token>
) -> Result<(Vec<Token>, SExp), String> {
- println!("{:?}", input.clone());
-
let mut current_token = input.pop_front();
match current_token {
diff --git a/src/sexp/display.rs b/src/sexp/display.rs
index e6dbb7c..238e8d4 100644
--- a/src/sexp/display.rs
+++ b/src/sexp/display.rs
@@ -26,20 +26,17 @@ impl fmt::Display for SExp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Atom(leaf) => write!(f, "{}", leaf.to_string()),
- SCons(a, b) => {
- match scons(a.clone(), b.clone()).into_vec() {
- Ok(l) => write!(
- f,
- "({})",
- l.into_iter()
- .map(|x| x.to_string())
- .collect::<Vec<String>>()
- .join(" ")
- ),
- Err(_) => write!(f, "({} {})", *a, *b),
- }
- }
+ SCons(a, b) =>
+ write!(
+ f,
+ "({})",
+ scons(a.clone(), b.clone())
+ .parts()
+ .into_iter()
+ .map(|x| x.to_string())
+ .collect::<Vec<String>>()
+ .join(" ")
+ )
}
}
-
}
diff --git a/src/sexp/step.rs b/src/sexp/step.rs
index 765e33c..1ab0291 100644
--- a/src/sexp/step.rs
+++ b/src/sexp/step.rs
@@ -209,4 +209,12 @@ impl SExp {
t => Err(format!("unimplemented: {:?}.step()", t)),
}
}
+
+
+ pub fn multistep(self) -> Result<Self, String> {
+ match self.clone().step()? {
+ x if x == self => Ok(self),
+ x => x.multistep()
+ }
+ }
}
diff --git a/src/type/check.rs b/src/type/check.rs
index 647ec4d..80cc974 100644
--- a/src/type/check.rs
+++ b/src/type/check.rs
@@ -192,7 +192,14 @@ impl Type {
match self {
Arrow(from, to) => {
- let generics = (*from).infer_generics_ctx(argtype, Vec::new())?;
+ let generics = match (*from).infer_generics_ctx(argtype, Vec::new()) {
+ Ok(x) => Ok(x),
+ Err(None) => Err(ArgumentsDontMatchGeneric {
+ argtype: argtype.clone(),
+ generictype: self.clone(),
+ }),
+ Err(Some(e)) => Err(e),
+ }?;
let mut restype = (**to).clone();
for (name, ty) in generics {
restype = restype.subst(&name, &ty);
@@ -211,7 +218,7 @@ impl Type {
&self,
argtype: &Type,
ctx: Vec<(String, Type)>
- ) -> Result<Vec<(String, Type)>, TypeError> {
+ ) -> Result<Vec<(String, Type)>, Option<TypeError>> {
match (self, argtype) {
(a, b) if a == b => Ok(ctx),
@@ -239,11 +246,7 @@ impl Type {
Ok(res)
},
- (_a, _b) => Err(InvalidArgList {
- arglist: Atom(Var("undefined".to_string())), // TODO: hacky as heck
- expected: self.clone(),
- found: argtype.clone(),
- }),
+ (_a, _b) => Err(None),
}
}
diff --git a/src/type/display.rs b/src/type/display.rs
index be51858..68d7fec 100644
--- a/src/type/display.rs
+++ b/src/type/display.rs
@@ -11,13 +11,13 @@ impl fmt::Display for Type {
/// ```rust
/// use melisp::r#type::{Type::*, util::*};
/// assert_eq!(Integer.to_string(), "Int".to_string());
- /// assert_eq!(arr(Integer, Integer).to_string(), "Int -> Int".to_string());
+ /// assert_eq!(arr(Integer, Integer).to_string(), "(Int -> Int)".to_string());
/// assert_eq!(List(vec![Integer, Integer, Integer]).to_string(), "(Int Int Int)".to_string());
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Integer => write!(f, "{}", "Int"),
- Arrow(a, b) => write!(f, "{} -> {}", a, b),
+ Arrow(a, b) => write!(f, "({} -> {})", a, b),
List(types) => write!(
f,
"({})",
@@ -50,7 +50,7 @@ impl fmt::Display for TypeError {
/// expected: arr(VarType("?".to_string()), VarType("?".to_string())),
/// found: Integer
/// }.to_string(),
- /// "invalid operator: '1'\nexpected: '? -> ?'\nfound: 'Int'".to_string()
+ /// "invalid operator: '1'\nexpected: '(? -> ?)'\nfound: 'Int'".to_string()
/// );
/// assert_eq!(
/// InvalidArgList {
@@ -64,6 +64,13 @@ impl fmt::Display for TypeError {
/// UnboundGeneric(String::from("?")).to_string(),
/// "unbound generic type: '?'".to_string()
/// );
+ /// assert_eq!(
+ /// ArgumentsDontMatchGeneric {
+ /// argtype: Integer,
+ /// generictype: arr(VarType("T".to_string()), Integer)
+ /// }.to_string(),
+ /// "incompatible argument type 'Int' and generic operator type '(T -> Int)'".to_string()
+ /// );
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -83,6 +90,13 @@ impl fmt::Display for TypeError {
arglist, expected, found
)
},
+ ArgumentsDontMatchGeneric { argtype, generictype } => {
+ write!(
+ f,
+ "incompatible argument type '{}' and generic operator type '{}'",
+ argtype, generictype
+ )
+ },
OtherError => write!(f, "uncategorized error"),
}
}
diff --git a/src/type/mod.rs b/src/type/mod.rs
index aeeff93..2454f31 100644
--- a/src/type/mod.rs
+++ b/src/type/mod.rs
@@ -45,6 +45,11 @@ pub enum TypeError {
found: Type,
},
+ ArgumentsDontMatchGeneric {
+ argtype: Type,
+ generictype: Type,
+ },
+
OtherError
}