diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 380cee0..e7cefe3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1,5 +1,5 @@ use std::ops::Range; -use parser::Expr; +use parser::types::{Expr, Typehint}; const INTRINSICS: [&str; 8] = [ "write", @@ -30,6 +30,7 @@ impl std::fmt::Display for Value { pub enum IRKind { Value { value: Value }, Vector { values: Vec }, + Tuple { values: Vec }, Unary { op: String, @@ -422,7 +423,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { Expr::String(value) => return_ok!(IRKind::Value { value: Value::String(value.clone()) }), Expr::Identifier(value) => return_ok!(IRKind::Value { value: Value::Ident(value.clone()) }), - Expr::Vector(values) => { + v @ Expr::Vector(values) | v @ Expr::Tuple(values) => { let mut lvalues = Vec::new(); for value in values { let value = expr_to_ir(&value.0); @@ -430,7 +431,11 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { lvalues.push(value.0.unwrap()); } - (Some(IRKind::Vector { values: lvalues }), None) + match v { + Expr::Vector(..) => return_ok!(IRKind::Vector { values: lvalues }), + Expr::Tuple(..) => return_ok!(IRKind::Tuple { values: lvalues }), + _ => unreachable!() + } }, // Probably will never happen because it is catched in parser @@ -442,16 +447,21 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { } } -fn gen_type_hint(type_hint: &str) -> String { +fn gen_type_hint(type_hint: &Typehint) -> String { match type_hint { - "int" => "number".to_string(), - "bool" => "boolean".to_string(), - "string" => "string".to_string(), - "void" => "void".to_string(), - // TODO: Un-hardcode types - "vec_int" => "number[]".to_string(), - "vec_bool" => "boolean[]".to_string(), - "vec_string" => "string[]".to_string(), - _ => { dbg!(type_hint); todo!() } + Typehint::Single(t) => match t.as_str() { + "int" => "number".to_string(), + "bool" => "boolean".to_string(), + _ => t.to_string() + }, + Typehint::Tuple(ts) => { + let mut types = Vec::new(); + for t in ts { + types.push(gen_type_hint(&t.0)); + } + format!("[{}]", types.join(", ")) + }, + Typehint::Vector(t) => format!("{}[]", gen_type_hint(&t.0)), + Typehint::Function(_args, _ret) => {dbg!(type_hint); todo!()}, // TODO: Function type } } diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index dd8b9e9..3cc9506 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -169,25 +169,4 @@ pub fn lexer() -> impl Parser, Error = Simple> { pub fn lex(src: String) -> (Option)>>, Vec>) { let (tokens, lex_error) = lexer().parse_recovery(src.as_str()); (tokens, lex_error) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn lex_let_simple() { - let (tokens, err) = lex("let x: Int = 1;".to_string()); - - assert_eq!(tokens, Some(vec![ - (Token::KwLet, 0..3), - (Token::Identifier("x".to_string()), 4..5), - (Token::Colon, 5..6), - (Token::Identifier("Int".to_string()), 7..10), - (Token::Assign, 11..12), - (Token::Int(1), 13..14), - (Token::SemiColon, 14..15), - ])); - assert_eq!(err, vec![]); - } -} +} \ No newline at end of file diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index fea717e..54617f0 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,53 +1,53 @@ use chumsky::{prelude::*, Stream}; use lexer::Token; -pub type Spanned = (T, std::ops::Range); +pub mod types; +use types::{Expr, Spanned, Typehint}; -#[derive(Clone, Debug)] -pub enum Expr { - Int(i64), Float(f64), Boolean(bool), - String(String), Identifier(String), +fn typehint_parser() -> impl Parser, Error = Simple> + Clone { + let single = filter_map(|span, token| match token { + Token::Identifier(s) => Ok((Typehint::Single(s), span)), + _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), + }); - Vector(Vec>), + let tuple = single + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by( + just(Token::OpenParen), + just(Token::CloseParen), + ) + .map_with_span(|args, span| { + (Typehint::Tuple(args), span) + }); - Unary { op: String, rhs: Box> }, - Binary { lhs: Box>, op: String, rhs: Box> }, - Call { name: Box>, args: Spanned>> }, - Pipeline { lhs: Box>, rhs: Box> }, - Intrinsic { name: Box>, args: Spanned>> }, + let vector = single + .delimited_by( + just(Token::OpenBracket), + just(Token::CloseBracket), + ) + .map_with_span(|arg, span| { + (Typehint::Vector(Box::new(arg)), span) + }); - Let { - public: bool, - name: Spanned, - type_hint: Spanned, - value: Box>, - mutable: bool, - }, - Fun { - public: bool, - name: Spanned, - type_hint: Spanned, - args: Spanned, Spanned)>>, - body: Box> - }, - Return { expr: Box> }, + let function = single + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by( + just(Token::Pipe), + just(Token::Pipe), + ) + .then_ignore(just(Token::Arrow)) + .then(single) + .map_with_span(|(args, ret), span| { + (Typehint::Function(args, Box::new(ret)), span) + }); - If { - cond: Box>, - body: Box>, - else_body: Box> - }, - Case { - expr: Box>, - cases: Spanned, Spanned)>>, - default: Box> - }, - Do { - body: Spanned>> - }, - - // Hole for positional argument(s) in piping - Hole(usize, usize), // The usize is the span of the hole (prob should be single but whatever) + single + .or(tuple) + .or(vector) + .or(function) + .labelled("type hint") } fn expr_parser() -> impl Parser>, Error = Simple> + Clone { @@ -83,9 +83,24 @@ fn expr_parser() -> impl Parser>, Error = Simple ) }); + let tuple = expr.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by( + just(Token::OpenParen), + just(Token::CloseParen), + ) + .map_with_span(|args, span| { + ( + Expr::Tuple(args), + span, + ) + }); + let atom = literal .or(identifier.map(|(s, span)| (Expr::Identifier(s), span))) .or(vector) + .or(tuple) .labelled("atom"); let call = atom.clone() @@ -219,7 +234,7 @@ fn expr_parser() -> impl Parser>, Error = Simple .then(just(Token::KwMut).or_not()) .then(identifier) .then_ignore(just(Token::Colon)) - .then(identifier) + .then(typehint_parser()) .then_ignore(just(Token::Assign)) .then(expr.clone()) .map(|((((public, mutable), name), type_hint), value)| { @@ -241,7 +256,7 @@ fn expr_parser() -> impl Parser>, Error = Simple .then( identifier .then_ignore(just(Token::Colon)) - .then(identifier) + .then(typehint_parser()) .delimited_by( just(Token::OpenParen), just(Token::CloseParen), @@ -249,7 +264,7 @@ fn expr_parser() -> impl Parser>, Error = Simple .repeated() ) .then_ignore(just(Token::Colon)) - .then(identifier) + .then(typehint_parser()) .then_ignore(just(Token::Assign)) .then(expr.clone()) .map(|((((public, name), args), type_hint), body)| { @@ -370,23 +385,4 @@ pub fn parse(tokens: Vec<(Token, std::ops::Range)>, len: usize) -> (Optio )); (ast, parse_error) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_simple() { - let (_, err) = parse(vec![ - (Token::KwLet, 0..3), - (Token::Identifier("x".to_string()), 4..5), - (Token::Colon, 5..6), - (Token::Identifier("Int".to_string()), 7..10), - (Token::Assign, 11..12), - (Token::Int(1), 13..14), - ], 15); - - assert_eq!(err, vec![]); - } -} +} \ No newline at end of file diff --git a/crates/parser/src/types.rs b/crates/parser/src/types.rs new file mode 100644 index 0000000..c20fc8c --- /dev/null +++ b/crates/parser/src/types.rs @@ -0,0 +1,58 @@ + +pub type Spanned = (T, std::ops::Range); + +#[derive(Clone, Debug)] +pub enum Typehint { + Single(String), // e.g. `int`, `bool`, `string` + Tuple(Vec>), // e.g. `(int, bool)` + Vector(Box>), // e.g. `[int]` + Function(Vec>, Box>), // e.g. `(a: int, b: bool) -> string`, `(b: int) -> [bool]` +} + +#[derive(Clone, Debug)] +pub enum Expr { + Int(i64), Float(f64), Boolean(bool), + String(String), Identifier(String), + + Tuple(Vec>), // Wait, its all Vec>? + Vector(Vec>), // Always have been + + Unary { op: String, rhs: Box> }, + Binary { lhs: Box>, op: String, rhs: Box> }, + Call { name: Box>, args: Spanned>> }, + Pipeline { lhs: Box>, rhs: Box> }, + Intrinsic { name: Box>, args: Spanned>> }, + + Let { + public: bool, + name: Spanned, + type_hint: Spanned, + value: Box>, + mutable: bool, + }, + Fun { + public: bool, + name: Spanned, + type_hint: Spanned, + args: Spanned, Spanned)>>, + body: Box> + }, + Return { expr: Box> }, + + If { + cond: Box>, + body: Box>, + else_body: Box> + }, + Case { + expr: Box>, + cases: Spanned, Spanned)>>, + default: Box> + }, + Do { + body: Spanned>> + }, + + // Hole for positional argument(s) in piping + Hole(usize, usize), // The usize is the span of the hole (prob should be single but whatever) +} \ No newline at end of file diff --git a/example/err/define_wrong_type.hz b/example/err/define_wrong_type.hz deleted file mode 100644 index 6d0cfb6..0000000 --- a/example/err/define_wrong_type.hz +++ /dev/null @@ -1,3 +0,0 @@ -let foo: int = "123" -let bar: string = 69 -let baz: bool = "true" \ No newline at end of file diff --git a/example/err/do_end_scope.hz b/example/err/do_end_scope.hz deleted file mode 100644 index 49f3cb0..0000000 --- a/example/err/do_end_scope.hz +++ /dev/null @@ -1,7 +0,0 @@ -fun main: void = do - do - let foo: string = "Hello" - @write(foo) - end - @write(foo) -- TODO: This is a runtime error -end \ No newline at end of file diff --git a/example/err/throw.hz b/example/err/throw.hz deleted file mode 100644 index 4a34e26..0000000 --- a/example/err/throw.hz +++ /dev/null @@ -1,3 +0,0 @@ -fun main: void = do - @throw("woopsie") -end \ No newline at end of file diff --git a/example/err/unknown_intrinsic.hz b/example/err/unknown_intrinsic.hz deleted file mode 100644 index 81ee0e4..0000000 --- a/example/err/unknown_intrinsic.hz +++ /dev/null @@ -1,3 +0,0 @@ -fun main: void = do - @writse() -- Unknown intrinsic -end \ No newline at end of file diff --git a/example/export.hz b/example/export.hz deleted file mode 100644 index 48a74a3..0000000 --- a/example/export.hz +++ /dev/null @@ -1,3 +0,0 @@ -pub fun print_something: void = @emit("console.log('something')") - -fun main: void = do end \ No newline at end of file diff --git a/example/if.hz b/example/if.hz deleted file mode 100644 index 0702348..0000000 --- a/example/if.hz +++ /dev/null @@ -1,15 +0,0 @@ -fun main: void = do - if true then - @write("True") - else - @write("False") - end - - if true then - do - @write("True") - end - else - @write("False") - end -end \ No newline at end of file diff --git a/example/match.hz b/example/match.hz deleted file mode 100644 index b18a9e3..0000000 --- a/example/match.hz +++ /dev/null @@ -1,12 +0,0 @@ -fun main: void = do - let foo: int = 3 - - match foo with - | 1 -> @write("One") - | 2 -> @write("Two") - | 3 -> do - @write("Three") - end - | else @write("idk") - end -end diff --git a/example/pipe.hz b/example/pipe.hz deleted file mode 100644 index e7641ef..0000000 --- a/example/pipe.hz +++ /dev/null @@ -1,14 +0,0 @@ -fun foo (xs: int): int = return xs + 1 -fun bar (xs: int) (x: int): int = return xs - x - -fun main: void = do - foo(69) -- 69 + 1 => 70 - |> bar(_, 1) -- '70 - 1 => 69 - |> @write(_) -- '69 => stdout - - @write("\n") - - foo(60) -- 60 + 1 => 61 - |> bar(130, _) -- 130 - '61 => 69 - |> @write(_) -- '69 => stdout -end \ No newline at end of file diff --git a/example/pipe2.hz b/example/pipe2.hz deleted file mode 100644 index 83c1cef..0000000 --- a/example/pipe2.hz +++ /dev/null @@ -1,19 +0,0 @@ -fun foo (xs: int) : int = return xs + 1 -fun bar (xs: int) (ys: int) : int = return xs + ys -fun baz (xs: int) (ys: int) (zs: int): int = return xs + ys + zs -fun qux (xs: int) : int = return xs - 1 -fun quux (xs: int) (xy: int) : int = return xs - 2 + xy - -fun main: void = do - 66 - |> foo(_) - |> bar(_, 1) - |> baz(1, 2, _) - |> qux(_) - |> quux(1, _) - |> @write(_) - - 210 - |> bar(_, _) - |> @write(_) -end \ No newline at end of file diff --git a/example/strings.hz b/example/strings.hz deleted file mode 100644 index be6eea4..0000000 --- a/example/strings.hz +++ /dev/null @@ -1,16 +0,0 @@ -fun main: void = do - let str: string = "Hello, World" - - -- Matching string(s) - match str with - | "Hello, World" -> @write("yes\n") - | else @write("no\n") - end - - -- Indexing character in string - let foo: string = @get(str, 0) - match foo with - | "H" -> @write("still yes") - | else @write("no H") - end -end diff --git a/example/tuples.hz b/example/tuples.hz new file mode 100644 index 0000000..cba831f --- /dev/null +++ b/example/tuples.hz @@ -0,0 +1,6 @@ +fun test (foo: int) (bar: int) : int = foo * bar +fun f (fn: |int, int| -> int) (x: int) : int = fn(x) + +fun main: void = do + let x : int = 20 +end \ No newline at end of file