summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJJ2023-04-11 03:20:09 +0000
committerJJ2023-04-11 10:19:23 +0000
commit438b0fca6a7803283606156735dc17e5c634f887 (patch)
tree30d2fde8c9f26883ade6b7d25cf840b1e2277be0
parent0d699337f42ab6a4a09840fd06af80df944cae94 (diff)
slight cleanups
-rw-r--r--src/main.rs6
-rw-r--r--src/parser.rs49
-rw-r--r--src/simple.rs58
-rw-r--r--tests/test_parser.rs38
4 files changed, 78 insertions, 73 deletions
diff --git a/src/main.rs b/src/main.rs
index bff203b..2a44397 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -21,7 +21,7 @@ fn main() {
input.clear();
stdin().read_line(&mut input).unwrap();
- match simple::infer(Context::new(), parser::parse(&input)) {
+ match simple::infer(Context::new(), parser::parse_lambda(&input).unwrap()) {
Ok(term) => println!("infers! {:?}", term),
Err(e) => println!("{:?}", e),
}
@@ -36,7 +36,7 @@ fn main() {
let kind = simple::infer(Context::new(), parser::parse(&input));
match kind {
Ok(kind) => {
- match simple::check(Context::new(), parser::parse(&input), kind) {
+ match simple::check(Context::new(), parser::parse_lambda(&input).unwrap(), kind) {
Ok(_) => println!("checks!"),
Err(e) => println!("{:?}", e),
}
@@ -51,7 +51,7 @@ fn main() {
input.clear();
stdin().read_line(&mut input).unwrap();
- match simple::execute(Context::new(), parser::parse(&input)) {
+ match simple::execute(Context::new(), parser::parse_lambda(&input).unwrap()) {
Ok(term) => println!("{:?}", term),
Err(e) => println!("{:?}", e)
}
diff --git a/src/parser.rs b/src/parser.rs
index edf906e..79e9ec8 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -2,13 +2,31 @@ use crate::ast::*;
// (λx:T.y): T z
pub fn parse(input: &str) -> Expression {
- return parse_str(input).expect("invalid expression");
+ match parse_lambda(input) {
+ Ok(expr) => return expr,
+ Err(e) => println!("invalid expression! {:?}", e)
+ }
+ return Expression::Constant { term: Term { val: 0, kind: Type::Empty } };
+}
+
+/// Parses a Nim-like language into an AST.
+pub fn parse_file(path: &str) -> Vec<Expression> {
+ match std::fs::read_to_string(path) {
+ Ok(file) => match lex(&file) {
+ Ok(input) => match parse_lang(&input) {
+ Ok(expr) => return expr,
+ Err(e) => println!("failed to parse file! {:?}", e),
+ },
+ Err(e) => println!("failed to lex file! {:?}", e),
+ },
+ Err(e) => println!("failed to read file! {:?}", e),
+ }
+ return Vec::new();
}
/// Parses a lambda-calculus-like language into an AST.
-pub fn parse_str(input: &str) -> Result<Expression, peg::error::ParseError<peg::str::LineCol>> {
- // this is kinda awful
- // i miss my nim pegs
+pub fn parse_lambda(input: &str) -> Result<Expression, peg::error::ParseError<peg::str::LineCol>> {
+ // this is kinda awful, i miss my simple nim pegs
peg::parser!{
grammar lambda() for str {
rule identifier() -> String
@@ -86,21 +104,12 @@ pub fn parse_str(input: &str) -> Result<Expression, peg::error::ParseError<peg::
= expression() ** ("\n"+)
}
}
- // assert_eq!(lambda::expression("(λx:bool.x)").unwrap(), lambda::expression("(λx: bool . x)").unwrap());
-
return lambda::expression(input.trim());
}
-/// Parses a Nim-like language into an AST.
-#[allow(unused_variables)]
-pub fn parse_file(path: &str) -> Vec<Expression> {
- todo!();
-}
-
/// Converts a whitespace-indented language into a regular bracketed language for matching with PEGs
/// Then, tokens are known to be separated by [\n ]+ (except strings. problem for later.)
pub fn lex(input: &str) -> Result<String, &'static str> {
-
#[derive(Eq, PartialEq)]
enum Previous {
Start,
@@ -119,6 +128,7 @@ pub fn lex(input: &str) -> Result<String, &'static str> {
let mut buffer = String::new();
let mut result = String::new();
+ // wow lexers are hard
for c in input.chars() {
match c {
'\n' => {
@@ -146,7 +156,7 @@ pub fn lex(input: &str) -> Result<String, &'static str> {
}
state.level -= indent_size;
result.push('\n');
- result.push_str(" ".repeat(state.level).as_str());
+ result.push_str(&" ".repeat(state.level));
result.push('}');
state.previous = Previous::Block;
}
@@ -155,7 +165,7 @@ pub fn lex(input: &str) -> Result<String, &'static str> {
return Err("unknown indentation error");
}
- result.push_str(" ".repeat(state.count).as_str());
+ result.push_str(&" ".repeat(state.count));
result.push_str(&buffer);
state.count = 0;
@@ -180,8 +190,15 @@ pub fn lex(input: &str) -> Result<String, &'static str> {
while state.level != 0 {
state.level -= 2;
result.push('\n');
- result.push_str(" ".repeat(state.level).as_str());
+ result.push_str(&" ".repeat(state.level));
result.push('}');
}
return Ok(result);
}
+
+/// Parses a simple language with bracket-based indentation and end-of-term semicolons.
+/// The lex() function can turn an indentation-based language into a language recognizable by this.
+#[allow(unused_variables)]
+pub fn parse_lang(input: &str) -> Result<Vec<Expression>, peg::error::ParseError<peg::str::LineCol>> {
+ todo!();
+}
diff --git a/src/simple.rs b/src/simple.rs
index 4419680..22708a4 100644
--- a/src/simple.rs
+++ b/src/simple.rs
@@ -8,32 +8,25 @@ pub fn check(context: Context, expression: Expression, target: Type) -> Result<(
match expression {
Expression::Annotation { expr, kind } => Err(("attempting to typecheck an annotation", context, target)),
// Bt-CheckInfer
- Expression::Constant { term } => {
- if term.kind == target {
- Ok(())
- } else {
- Err(("constant is of wrong type", context, target))
- }
+ Expression::Constant { term } => match term.kind == target {
+ true => Ok(()),
+ false => Err(("constant is of wrong type", context, target))
},
// Bt-CheckInfer
- Expression::Variable { id } => {
- // in the future: extend to closures? nah probably not
- match context.get(&id) {
- Some(term) if term.kind == target => Ok(()),
- Some(_) => Err(("variable is of wrong type", context, target)),
- None => Err(("failed to find variable in context", context, target))
- }
+ // in the future: extend to closures? nah probably not
+ Expression::Variable { id } => match context.get(&id) {
+ Some(term) if term.kind == target => Ok(()),
+ Some(_) => Err(("variable is of wrong type", context, target)),
+ None => Err(("failed to find variable in context", context, target))
},
// Bt-Abs
- Expression::Abstraction { param, func } => {
- match target {
- Type::Function { from, to } => {
- let mut context = context;
- context.insert(param, Term { val: 0, kind: *from }); // hack
- return check(context, *func, *to);
- },
- _ => Err(("attempting to check an abstraction with a non-function type", context, target))
- }
+ Expression::Abstraction { param, func } => match target {
+ Type::Function { from, to } => {
+ let mut context = context;
+ context.insert(param, Term { val: 0, kind: *from }); // hack
+ return check(context, *func, *to);
+ },
+ _ => Err(("attempting to check an abstraction with a non-function type", context, target))
},
Expression::Application { func, arg } => Err(("attempting to check an application", context, target)),
// T-If
@@ -54,21 +47,16 @@ pub fn infer(context: Context, expression: Expression) -> Result<Type, (&'static
// Bt-True / Bt-False / etc
Expression::Constant { term } => Ok(term.kind),
// Bt-Var
- Expression::Variable { id } => {
- match context.get(&id) {
- Some(term) => Ok(term.clone().kind),
- None => Err(("failed to find variable in context", context, Type::Empty))
- }
+ Expression::Variable { id } => match context.get(&id) {
+ Some(term) => Ok(term.clone().kind),
+ None => Err(("failed to find variable in context", context, Type::Empty))
},
// Bt-App
- Expression::Application { func, arg } => {
- let expr = infer(context.clone(), *func)?;
- match expr {
- Type::Function { from, to } => {
- check(context, *arg, *from).map(|x| *to)
- },
- _ => Err(("application abstraction is not a function type", context, Type::Empty))
- }
+ Expression::Application { func, arg } => match infer(context.clone(), *func)? {
+ Type::Function { from, to } => {
+ check(context, *arg, *from).map(|x| *to)
+ },
+ _ => Err(("application abstraction is not a function type", context, Type::Empty))
},
Expression::Abstraction { param, func } => Err(("attempting to infer from an abstraction", context, Type::Empty)),
// idk
diff --git a/tests/test_parser.rs b/tests/test_parser.rs
index 54311b8..b096597 100644
--- a/tests/test_parser.rs
+++ b/tests/test_parser.rs
@@ -4,39 +4,39 @@ use chrysanthemum::util::*;
#[test]
fn test_simple_phrases() {
- assert_eq!(parse_str("123"), Ok(Const(123, Type::Empty)));
- assert_eq!(parse_str("x12"), Ok(Var("x12")));
- assert_eq!(parse_str("x12x2"), Ok(Var("x12x2")));
+ assert_eq!(parse_lambda("123"), Ok(Const(123, Type::Empty)));
+ assert_eq!(parse_lambda("x12"), Ok(Var("x12")));
+ assert_eq!(parse_lambda("x12x2"), Ok(Var("x12x2")));
// so i _don't_ want these to be valid identifiers:
// but i actually have no idea why my peg is rejecting them lmao
- assert!(parse_str("12x").is_err());
- assert!(parse_str("12x23").is_err());
+ assert!(parse_lambda("12x").is_err());
+ assert!(parse_lambda("12x23").is_err());
}
#[test]
fn test_simple_annotations() {
- assert_eq!(parse_str("t: int"), Ok(Ann(Var("t"), Type::Integer)));
- assert_eq!(parse_str("12: nat"), Ok(Ann(Const(12, Type::Empty), Type::Natural)));
- assert!(parse_str("t: fake").is_err());
+ assert_eq!(parse_lambda("t: int"), Ok(Ann(Var("t"), Type::Integer)));
+ assert_eq!(parse_lambda("12: nat"), Ok(Ann(Const(12, Type::Empty), Type::Natural)));
+ assert!(parse_lambda("t: fake").is_err());
}
#[test]
fn test_simple_expressions() {
- assert_eq!(parse_str("λx.y"), Ok(Abs("x", Var("y"))));
- assert_eq!(parse_str("λ x.y"), Ok(Abs("x", Var("y"))));
- assert_eq!(parse_str("λx.y"), Ok(Abs("x", Var("y"))));
- assert_eq!(parse_str("lambda x . y"), Ok(Abs("x", Var("y"))));
- assert!(parse_str("(λx.y)").is_err()); // fixme: should be fine
- assert_eq!(parse_str("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x"))));
- assert_eq!(parse_str("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x"))));
- assert_eq!(parse_str("if x then y else z"), Ok(Cond(Var("x"), Var("y"), Var("z"))));
- assert_eq!(parse_str("if xeme then yak else zebra"), Ok(Cond(Var("xeme"), Var("yak"), Var("zebra"))));
- assert_eq!(parse_str("if 413 then 612 else 1025"), Ok(Cond(Const(413, Type::Empty), Const(612, Type::Empty), Const(1025, Type::Empty)))); // invalid, but should parse
+ assert_eq!(parse_lambda("λx.y"), Ok(Abs("x", Var("y"))));
+ assert_eq!(parse_lambda("λ x.y"), Ok(Abs("x", Var("y"))));
+ assert_eq!(parse_lambda("λx.y"), Ok(Abs("x", Var("y"))));
+ assert_eq!(parse_lambda("lambda x . y"), Ok(Abs("x", Var("y"))));
+ assert!(parse_lambda("(λx.y)").is_err()); // fixme: should be fine
+ assert_eq!(parse_lambda("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x"))));
+ assert_eq!(parse_lambda("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x"))));
+ assert_eq!(parse_lambda("if x then y else z"), Ok(Cond(Var("x"), Var("y"), Var("z"))));
+ assert_eq!(parse_lambda("if xeme then yak else zebra"), Ok(Cond(Var("xeme"), Var("yak"), Var("zebra"))));
+ assert_eq!(parse_lambda("if 413 then 612 else 1025"), Ok(Cond(Const(413, Type::Empty), Const(612, Type::Empty), Const(1025, Type::Empty)))); // invalid, but should parse
}
#[test]
fn test_complex_expressions() {
- assert_eq!(parse_str("(λy.if y then 0 else 1) z"), Ok(App(Abs("y", Cond(Var("y"), Const(0, Type::Empty), Const(1, Type::Empty))), Var("z"))));
+ assert_eq!(parse_lambda("(λy.if y then 0 else 1) z"), Ok(App(Abs("y", Cond(Var("y"), Const(0, Type::Empty), Const(1, Type::Empty))), Var("z"))));
}
#[test]