diff options
author | JJ | 2023-04-11 03:20:09 +0000 |
---|---|---|
committer | JJ | 2023-04-11 10:19:23 +0000 |
commit | 438b0fca6a7803283606156735dc17e5c634f887 (patch) | |
tree | 30d2fde8c9f26883ade6b7d25cf840b1e2277be0 | |
parent | 0d699337f42ab6a4a09840fd06af80df944cae94 (diff) |
slight cleanups
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/parser.rs | 49 | ||||
-rw-r--r-- | src/simple.rs | 58 | ||||
-rw-r--r-- | tests/test_parser.rs | 38 |
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] |