diff --git a/src/parser/cursor/lit.rs b/src/parser/cursor/lit.rs index ae16f79..19997f5 100644 --- a/src/parser/cursor/lit.rs +++ b/src/parser/cursor/lit.rs @@ -1,3 +1,5 @@ +use crate::parser::{DisplayCtx, FmtNode}; + use super::Token; #[derive(PartialEq)] @@ -24,3 +26,9 @@ impl std::fmt::Display for Lit { } } } + +impl FmtNode for Lit { + fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result { + write!(f, "{self}") + } +} diff --git a/src/parser/cursor/mod.rs b/src/parser/cursor/mod.rs index e9fc466..fcdc6ba 100644 --- a/src/parser/cursor/mod.rs +++ b/src/parser/cursor/mod.rs @@ -54,6 +54,10 @@ impl<'a> Cursor<'a> { self.next().ok_or_else(CompilerMsg::unexpected_eof) } + pub fn expect_peek(&self) -> Result<&Token, CompilerMsg> { + self.peek().ok_or_else(CompilerMsg::unexpected_eof) + } + pub fn expect(&mut self, token: Token) -> Result { let next = self.expect_next()?; if next == token { diff --git a/src/parser/cursor/token.rs b/src/parser/cursor/token.rs index 6c3b7bc..127c60b 100644 --- a/src/parser/cursor/token.rs +++ b/src/parser/cursor/token.rs @@ -28,6 +28,8 @@ def_tokens! { Hash: "#", } keyword { + Let: "let", + Do: "do", Fn: "fn", If: "if", Loop: "loop", diff --git a/src/parser/node/parse.rs b/src/parser/node/parse.rs index 7f26562..4d6e552 100644 --- a/src/parser/node/parse.rs +++ b/src/parser/node/parse.rs @@ -15,7 +15,7 @@ pub trait Parsable: Sized + Node { pub struct ParseCtx<'a> { start: usize, cursor: Cursor<'a>, - nodes: Nodes, + pub nodes: Nodes, } impl<'a> ParseCtx<'a> { diff --git a/src/parser/nodes/body.rs b/src/parser/nodes/body.rs index cd4d8ba..e4d1c4d 100644 --- a/src/parser/nodes/body.rs +++ b/src/parser/nodes/body.rs @@ -1,13 +1,13 @@ use super::*; pub struct Body { - pub exprs: Vec>, + pub items: Vec>, pub final_semicolon: bool, } impl Parsable for Body { fn parse(ctx: &mut ParseCtx) -> Result { - let mut exprs = Vec::new(); + let mut items = Vec::new(); fn at_end(ctx: &mut ParseCtx) -> bool { ctx.peek().is_none_or(|t| *t == Token::CloseCurly) } @@ -15,14 +15,19 @@ impl Parsable for Body { if at_end(ctx) { break true; } - exprs.push(ctx.parse()?); + let item: Id = ctx.parse()?; + let needs_semicolon = item.needs_semicolon(&ctx.nodes); + items.push(item); if at_end(ctx) { break false; } - ctx.expect(Token::Semicolon)?; + if needs_semicolon { + ctx.expect(Token::Semicolon)?; + } + while ctx.next_if(Token::Semicolon) {} }; Ok(Self { - exprs, + items, final_semicolon, }) } @@ -31,15 +36,27 @@ impl Parsable for Body { impl FmtNode for Body { fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { // surely there's a better way to do this - if let Some((last, rest)) = self.exprs.split_last() { + if let Some((last, rest)) = self.items.split_last() { for &i in rest { - writeln!(f, "{}{};", " ".repeat(ctx.indent), i.dsp(ctx))?; - } - if self.final_semicolon { - writeln!(f, "{}{};", " ".repeat(ctx.indent), last.dsp(ctx))?; - } else { - writeln!(f, "{}{}", " ".repeat(ctx.indent), last.dsp(ctx))?; + writeln!( + f, + "{}{}{}", + " ".repeat(ctx.indent), + i.dsp(ctx), + if i.needs_semicolon(ctx.nodes) { + ";" + } else { + "" + } + )?; } + writeln!( + f, + "{}{}{}", + " ".repeat(ctx.indent), + last.dsp(ctx), + if self.final_semicolon { ";" } else { "" } + )?; } Ok(()) } diff --git a/src/parser/nodes/expr.rs b/src/parser/nodes/expr.rs index d0efc45..0fed282 100644 --- a/src/parser/nodes/expr.rs +++ b/src/parser/nodes/expr.rs @@ -16,11 +16,6 @@ pub enum Expr { target: Id, val: Id, }, - Define { - target: Id, - ty: Option>, - val: Id, - }, If { cond: Id, body: Id, @@ -45,16 +40,6 @@ impl Parsable for Expr { let val = ctx.parse_with(Self::unit)?; Expr::Assign { target, val } } - Token::Colon => { - let target = ctx.push_adv(res); - let mut ty = None; - if !ctx.next_if(Token::Equal) { - ty = Some(ctx.parse()?); - ctx.expect(Token::Equal)?; - } - let val = ctx.parse_with(Self::unit)?; - Expr::Define { target, ty, val } - } Token::OpenParen => { let target = ctx.push_adv(res); let args = ctx.list(Token::Comma, Token::CloseParen)?; @@ -76,12 +61,12 @@ impl Expr { Token::Fn => Self::Fn(ctx.parse()?), Token::If => { let cond = ctx.parse()?; - let body = cond_body(cond, ctx)?; + let body = cond_body(ctx)?; Self::If { cond, body } } Token::While => { let cond = ctx.parse()?; - let body = cond_body(cond, ctx)?; + let body = cond_body(ctx)?; Self::While { cond, body } } Token::Loop => { @@ -110,6 +95,10 @@ impl Expr { matches!(self, Expr::Group(_)) } + pub fn is_block(&self) -> bool { + matches!(self, Expr::Block(_)) + } + pub fn block(ctx: &mut ParseCtx) -> Result { ctx.expect(Token::OpenCurly)?; let id = ctx.parse()?; @@ -118,16 +107,32 @@ impl Expr { } } -fn cond_body(cond: Id, ctx: &mut ParseCtx) -> Result, CompilerMsg> { - if ctx[cond].is_group() { +fn cond_body(ctx: &mut ParseCtx) -> Result, CompilerMsg> { + if ctx.next_if(Token::Do) { ctx.parse() } else { ctx.parse_with(Expr::block) } } +impl Id { + pub fn ends_with_block(&self, nodes: &Nodes) -> bool { + match nodes[self] { + Expr::Block(..) => true, + Expr::Loop { body } + | Expr::While { body, .. } + | Expr::If { body, .. } + | Expr::Negate(body) + | Expr::Assign { val: body, .. } => body.ends_with_block(nodes), + Expr::Fn(f) => f.ends_with_block(nodes), + _ => false, + } + } +} + impl FmtNode for Expr { fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result { + let do_ = |id: Id| if ctx.nodes[id].is_block() { "" } else { "do " }; match *self { Self::Ident(id) => id.fmt(f, ctx), Self::Group(id) => write!(f, "({})", id.dsp(ctx)), @@ -142,27 +147,18 @@ impl FmtNode for Expr { Self::Assign { target, val } => { write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx)) } - Self::Define { target, ty, val } => { - target.fmt(f, ctx)?; - if let Some(ty) = ty { - write!(f, ": {} ", ty.dsp(ctx))?; - } else { - write!(f, " :")?; - } - write!(f, "= {}", val.dsp(ctx)) - } Self::If { cond, body } => { - write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx)) + write!(f, "if {} {}{}", cond.dsp(ctx), do_(body), body.dsp(ctx)) } Self::While { cond, body } => { - write!(f, "while {} {}", cond.dsp(ctx), body.dsp(ctx)) + write!(f, "while {} {}{}", cond.dsp(ctx), do_(body), body.dsp(ctx)) } Self::Loop { body } => { write!(f, "loop {}", body.dsp(ctx)) } Self::Block(body) => { write!(f, "{{")?; - if !ctx.nodes[body].exprs.is_empty() { + if !ctx.nodes[body].items.is_empty() { writeln!(f)?; ctx.indent += 3; body.fmt(f, ctx)?; @@ -173,9 +169,3 @@ impl FmtNode for Expr { } } } - -impl FmtNode for Lit { - fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result { - write!(f, "{self}") - } -} diff --git a/src/parser/nodes/func.rs b/src/parser/nodes/func.rs index aaa004c..8d76eb4 100644 --- a/src/parser/nodes/func.rs +++ b/src/parser/nodes/func.rs @@ -40,3 +40,9 @@ impl FmtNode for Func { Ok(()) } } + +impl Id { + pub fn ends_with_block(&self, nodes: &Nodes) -> bool { + nodes[self].body.ends_with_block(nodes) + } +} diff --git a/src/parser/nodes/item.rs b/src/parser/nodes/item.rs new file mode 100644 index 0000000..3c850d0 --- /dev/null +++ b/src/parser/nodes/item.rs @@ -0,0 +1,58 @@ +use super::*; + +pub enum Item { + Let { + name: Id, + ty: Option>, + val: Id, + }, + Struct(Id), + Expr(Id), +} + +impl Parsable for Item { + fn parse(ctx: &mut ParseCtx) -> Result { + Ok(match ctx.expect_peek()? { + Token::Let => { + ctx.next(); + let name = ctx.parse()?; + let mut ty = None; + if ctx.next_if(Token::Colon) { + ty = Some(ctx.parse()?); + } + ctx.expect(Token::Equal)?; + let val = ctx.parse()?; + Self::Let { name, ty, val } + } + _ => Self::Expr(ctx.parse()?), + }) + } +} + +impl FmtNode for Item { + fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { + match self { + Item::Let { name, ty, val } => { + write!(f, "let {}", name.dsp(ctx))?; + if let Some(ty) = ty { + write!(f, ": {}", ty.dsp(ctx))?; + } + write!(f, " = {}", val.dsp(ctx))?; + } + Item::Expr(id) => id.fmt(f, ctx)?, + } + Ok(()) + } +} + +impl Id { + pub fn ends_with_block(&self, nodes: &Nodes) -> bool { + match nodes[self] { + Item::Let { name, ty, val } => val.ends_with_block(nodes), + Item::Expr(id) => id.ends_with_block(nodes), + } + } + pub fn needs_semicolon(&self, nodes: &Nodes) -> bool { + !self.ends_with_block(nodes) + } +} diff --git a/src/parser/nodes/mod.rs b/src/parser/nodes/mod.rs index 585a687..b181d92 100644 --- a/src/parser/nodes/mod.rs +++ b/src/parser/nodes/mod.rs @@ -2,13 +2,17 @@ mod body; mod expr; mod func; mod ident; +mod item; mod param; +mod struct_; mod ty; pub use body::*; pub use expr::*; pub use func::*; pub use ident::*; +pub use item::*; pub use param::*; +pub use struct_::*; pub use ty::*; use super::{DisplayCtx, FmtNode, Id, Lit, Node, NodeVec, Parsable, ParseCtx, Token}; @@ -22,6 +26,8 @@ def_nodes! { types: Type, funcs: Func, params: Param, + items: Item, + structs: Struct, } macro_rules! def_nodes { diff --git a/src/parser/nodes/struct_.rs b/src/parser/nodes/struct_.rs new file mode 100644 index 0000000..eaccd5b --- /dev/null +++ b/src/parser/nodes/struct_.rs @@ -0,0 +1,14 @@ +use super::*; + +pub struct Struct { + name: String, + fields: Vec, +} + +impl Parsable for Struct { + fn parse(ctx: &mut ParseCtx) -> Result { + } +} + +pub struct Field { +} diff --git a/test/main.lang b/test/main.lang index cbd678e..47ca136 100644 --- a/test/main.lang +++ b/test/main.lang @@ -1,7 +1,11 @@ -x :i32 = 3; +let x: i32 = 3; while true { print("hello"); print(x); }; -y := fn(x: i32) -> i32 {x}; +if x { + print("hello"); +} + +let y = fn(x: i32) -> i32 {x};