diff --git a/hblang/examples/structs.hb b/hblang/examples/structs.hb new file mode 100644 index 0000000..68915ee --- /dev/null +++ b/hblang/examples/structs.hb @@ -0,0 +1,9 @@ +Ty := struct { + a: int + b: int +} + +main := fn(): int { + inst := Ty.{ a: 1, b: 2 }; + return inst.a + inst.b; +} diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 129b582..2851bbc 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -915,5 +915,6 @@ mod tests { loops => include_str!("../examples/loops.hb"); fb_driver => include_str!("../examples/fb_driver.hb"); pointers => include_str!("../examples/pointers.hb"); + structs => include_str!("../examples/structs.hb"); } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 3698108..e733962 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -91,17 +91,20 @@ gen_token_kind! { Break = b"break", Continue = b"continue", Fn = b"fn", + Struct = b"struct", #[punkt] - LParen = b'(', - RParen = b')', - LBrace = b'{', - RBrace = b'}', - Semi = b';', - Colon = b':', - Comma = b',', + LParen = "(", + RParen = ")", + LBrace = "{", + RBrace = "}", + Semi = ";", + Colon = ":", + Comma = ",", + Dot = ".", + Ctor = ".{", #[ops] #[prec = 1] - Decl = ":=", + Decl = ":=", Assign = "=", #[prec = 21] Le = "<=", @@ -109,10 +112,10 @@ gen_token_kind! { #[prec = 22] Amp = "&", #[prec = 23] - Plus = "+", + Plus = "+", Minus = "-", #[prec = 24] - Star = "*", + Star = "*", FSlash = "/", } } @@ -202,6 +205,8 @@ impl<'a> Iterator for Lexer<'a> { b':' if self.advance_if(b'=') => T::Decl, b':' => T::Colon, b',' => T::Comma, + b'.' if self.advance_if(b'{') => T::Ctor, + b'.' => T::Dot, b';' => T::Semi, b'=' if self.advance_if(b'=') => T::Eq, b'=' => T::Assign, diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 8d0a769..0ebeb75 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -139,6 +139,18 @@ impl<'a, 'b> Parser<'a, 'b> { let frame = self.idents.len(); let token = self.next(); let mut expr = match token.kind { + T::Struct => E::Struct { + pos: token.start, + fields: { + self.expect_advance(T::LBrace); + self.collect_list(T::Comma, T::RBrace, |s| { + let name = s.expect_advance(T::Ident); + s.expect_advance(T::Colon); + let ty = s.expr(); + (s.lexer.slice(name.range()), ty) + }) + }, + }, T::Ident => { let (id, last) = self.resolve_ident(token, self.token.kind == T::Decl); let name = self.lexer.slice(token.range()); @@ -207,14 +219,32 @@ impl<'a, 'b> Parser<'a, 'b> { }; loop { - expr = match self.token.kind { - T::LParen => { - self.next(); - Expr::Call { - func: self.arena.alloc(expr), - args: self.collect_list(T::Comma, T::RParen, Self::expr), - } - } + let token = self.token.kind; + if matches!(token, T::LParen | T::Ctor | T::Dot) { + self.next(); + } + + expr = match token { + T::LParen => Expr::Call { + func: self.arena.alloc(expr), + args: self.collect_list(T::Comma, T::RParen, Self::expr), + }, + T::Ctor => E::Ctor { + ty: self.arena.alloc(expr), + fields: self.collect_list(T::Comma, T::RBrace, |s| { + let name = s.expect_advance(T::Ident); + s.expect_advance(T::Colon); + let val = s.expr(); + (s.lexer.slice(name.range()), val) + }), + }, + T::Dot => E::Field { + ty: self.arena.alloc(expr), + field: { + let token = self.expect_advance(T::Ident); + self.lexer.slice(token.range()) + }, + }, _ => break, } } @@ -313,16 +343,16 @@ pub enum Expr<'a> { Closure { pos: u32, args: &'a [Arg<'a>], - ret: &'a Expr<'a>, - body: &'a Expr<'a>, + ret: &'a Self, + body: &'a Self, }, Call { - func: &'a Expr<'a>, - args: &'a [Expr<'a>], + func: &'a Self, + args: &'a [Self], }, Return { pos: u32, - val: Option<&'a Expr<'a>>, + val: Option<&'a Self>, }, Ident { name: &'a str, @@ -331,31 +361,43 @@ pub enum Expr<'a> { }, Block { pos: u32, - stmts: &'a [Expr<'a>], + stmts: &'a [Self], }, Number { pos: u32, value: u64, }, BinOp { - left: &'a Expr<'a>, + left: &'a Self, op: TokenKind, - right: &'a Expr<'a>, + right: &'a Self, }, If { pos: u32, - cond: &'a Expr<'a>, - then: &'a Expr<'a>, - else_: Option<&'a Expr<'a>>, + cond: &'a Self, + then: &'a Self, + else_: Option<&'a Self>, }, Loop { pos: u32, - body: &'a Expr<'a>, + body: &'a Self, }, UnOp { pos: u32, op: TokenKind, - val: &'a Expr<'a>, + val: &'a Self, + }, + Struct { + pos: u32, + fields: &'a [(&'a str, Self)], + }, + Ctor { + ty: &'a Self, + fields: &'a [(&'a str, Self)], + }, + Field { + ty: &'a Self, + field: &'a str, }, } @@ -366,6 +408,29 @@ impl<'a> std::fmt::Display for Expr<'a> { } match *self { + Self::Field { ty, field } => write!(f, "{}.{}", ty, field), + Self::Struct { fields, .. } => { + write!(f, "struct {{")?; + let first = &mut true; + for (name, ty) in fields { + if !std::mem::take(first) { + write!(f, ", ")?; + } + write!(f, "{}: {}", name, ty)?; + } + write!(f, "}}") + } + Self::Ctor { ty, fields } => { + write!(f, "{} {{", ty)?; + let first = &mut true; + for (name, val) in fields { + if !std::mem::take(first) { + write!(f, ", ")?; + } + write!(f, "{}: {}", name, val)?; + } + write!(f, "}}") + } Self::UnOp { op, val, .. } => write!(f, "{}{}", op, val), Self::Break { .. } => write!(f, "break;"), Self::Continue { .. } => write!(f, "continue;"), diff --git a/hblang/test.bin b/hblang/test.bin index 2e76a9a..3b2a001 100644 Binary files a/hblang/test.bin and b/hblang/test.bin differ