diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1f35542..d936f91 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -9,10 +9,10 @@ const INTRINSICS: [&str; 5] = [ "time", ]; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum IRKind { Define { name: String, type_hint: String, value: Box }, Fun { name: String, return_type_hint: String, args: Vec<(String, String)>, body: Box }, @@ -110,28 +110,63 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { match &rhs.0 { call @ Expr::Call { name, args } | call @ Expr::Intrinsic { name, args } => { - let name = match &name.0 { + let cname = match &name.0 { Expr::Identifier(s) => s.clone(), // Should never happen because the parser should have caught this _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None })) }; - let call = expr_to_ir(&call); - if_err_return!(call.1); + // Remove all `Hole`(s) from the args + let index = args.0.iter().position(|arg| match arg.0 { + Expr::Hole => true, + _ => false + }); - let mut largs = Vec::new(); - for arg in &args.0 { - let arg = expr_to_ir(&arg.0); - if_err_return!(arg.1); - largs.push(arg.0.unwrap()); + if let None = index { + return (None, Some(LoweringError { + span: rhs.1.clone(), + message: "Expected hole in piping".to_string(), + note: None + })); } - let mut args = vec![lhs_ir.0.unwrap()]; - args.append(&mut largs); + let mut new_args = args.0.clone(); + new_args.remove(index.unwrap()); - let ir_kind = match call.0.unwrap() { - IRKind::Call { .. } => IRKind::Call { name, args }, - IRKind::Intrinsic { .. } => IRKind::Intrinsic { name, args }, + // Make a new call expression with the new args + let new_call = match call { + Expr::Call { name, args } => Expr::Call{ + name: name.clone(), + args: (new_args, args.1.clone()) + }, + Expr::Intrinsic { name, args } => Expr::Intrinsic { + name: name.clone(), + args: (new_args, args.1.clone()) + }, + _ => unreachable!() + }; + let new_call = expr_to_ir(&new_call); + if_err_return!(new_call.1); + + // Lower all args + let mut largs = Vec::new(); + for arg in &args.0 { + match arg.0 { + Expr::Hole => { + largs.push(lhs_ir.0.clone().unwrap()); + }, + _ => { + let arg = expr_to_ir(&arg.0); + if_err_return!(arg.1); + largs.push(arg.0.unwrap()); + } + } + } + + // Match the call to the right IRKind + let ir_kind = match new_call.0.unwrap() { + IRKind::Call { .. } => IRKind::Call { name: cname, args: largs }, + IRKind::Intrinsic { .. } => IRKind::Intrinsic { name: cname, args: largs }, _ => unreachable!() }; @@ -235,6 +270,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { Expr::Boolean(value) => (Some(IRKind::Value { value: Value::Boolean(*value) }), None), Expr::String(value) => (Some(IRKind::Value { value: Value::String(value.clone()) }), None), Expr::Identifier(value) => (Some(IRKind::Value { value: Value::Ident(value.clone()) }), None), + Expr::Hole => (None, None), _ => { dbg!(expr); todo!() } } } diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index 5704476..e470253 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -23,6 +23,7 @@ pub enum Token { Colon, SemiColon, OpenParen, CloseParen, At, + Hole, } impl std::fmt::Display for Token { @@ -61,6 +62,7 @@ impl std::fmt::Display for Token { Token::OpenParen => write!(f, "("), Token::CloseParen => write!(f, ")"), Token::At => write!(f, "@"), + Token::Hole => write!(f, "_"), } } } @@ -75,7 +77,7 @@ pub fn lexer() -> impl Parser, Error = Simple> { .then_ignore(just('"')) .collect::() .map(Token::String); - + let symbol = choice(( just('+').to(Token::Plus), just('-').to(Token::Minus), @@ -89,7 +91,7 @@ pub fn lexer() -> impl Parser, Error = Simple> { just('<').to(Token::Less), just('>').to(Token::Greater), - + just('=').to(Token::Assign), just('.').to(Token::Dot), just(',').to(Token::Comma), @@ -98,6 +100,7 @@ pub fn lexer() -> impl Parser, Error = Simple> { just('(').to(Token::OpenParen), just(')').to(Token::CloseParen), just('@').to(Token::At), + just('_').to(Token::Hole), )); let keyword = text::ident().map(|s: String| match s.as_str() { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index f148630..808806d 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -35,6 +35,8 @@ pub enum Expr { Do { body: Vec> }, + + Hole, } fn expr_parser() -> impl Parser>, Error = Simple> + Clone { @@ -47,6 +49,7 @@ fn expr_parser() -> impl Parser>, Error = Simple Token::Int(i) => Ok((Expr::Int(i), span)), Token::Boolean(b) => Ok((Expr::Boolean(b), span)), Token::String(s) => Ok((Expr::String(s), span)), + Token::Hole => Ok((Expr::Hole, span)), _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), }).labelled("literal"); diff --git a/example/pipe.hz b/example/pipe.hz index 5a63b35..fd8d3e1 100644 --- a/example/pipe.hz +++ b/example/pipe.hz @@ -2,13 +2,13 @@ fun foo (xs: int): int = return xs + 1; fun bar (xs: int) (x: int): int = return xs - x; fun main: int = do - foo(69) - |> bar(1) - |> @write(); + foo(69) -- 69 + 1 => 70 + |> bar(_, 1) -- '70 - 1 => 69 + |> @write(_); -- '69 => stdout @write("\n"); - 68 - |> foo() - |> @write(); + foo(60) -- 60 + 1 => 61 + |> bar(130, _) -- 130 - '61 => 69 + |> @write(_); -- '69 => stdout end; \ No newline at end of file diff --git a/example/quine.hz b/example/quine.hz index 62451d2..2c300d2 100644 --- a/example/quine.hz +++ b/example/quine.hz @@ -1,4 +1,4 @@ fun main: int = do @read_file("./example/quine.hz") -- Run this from root folder - |> @write(); + |> @write(_); end; \ No newline at end of file diff --git a/example/time.hz b/example/time.hz index e1a8ed9..a9efd71 100644 --- a/example/time.hz +++ b/example/time.hz @@ -1,4 +1,4 @@ fun main: int = do @time() - |> @write(); + |> @write(_); end; \ No newline at end of file