This commit is contained in:
2026-04-17 00:09:00 -04:00
parent 83edad0cd8
commit e5ae506a84
11 changed files with 161 additions and 52 deletions
+8
View File
@@ -1,3 +1,5 @@
use crate::parser::{DisplayCtx, FmtNode};
use super::Token; use super::Token;
#[derive(PartialEq)] #[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}")
}
}
+4
View File
@@ -54,6 +54,10 @@ impl<'a> Cursor<'a> {
self.next().ok_or_else(CompilerMsg::unexpected_eof) 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<Token, CompilerMsg> { pub fn expect(&mut self, token: Token) -> Result<Token, CompilerMsg> {
let next = self.expect_next()?; let next = self.expect_next()?;
if next == token { if next == token {
+2
View File
@@ -28,6 +28,8 @@ def_tokens! {
Hash: "#", Hash: "#",
} }
keyword { keyword {
Let: "let",
Do: "do",
Fn: "fn", Fn: "fn",
If: "if", If: "if",
Loop: "loop", Loop: "loop",
+1 -1
View File
@@ -15,7 +15,7 @@ pub trait Parsable: Sized + Node {
pub struct ParseCtx<'a> { pub struct ParseCtx<'a> {
start: usize, start: usize,
cursor: Cursor<'a>, cursor: Cursor<'a>,
nodes: Nodes, pub nodes: Nodes,
} }
impl<'a> ParseCtx<'a> { impl<'a> ParseCtx<'a> {
+29 -12
View File
@@ -1,13 +1,13 @@
use super::*; use super::*;
pub struct Body { pub struct Body {
pub exprs: Vec<Id<Expr>>, pub items: Vec<Id<Item>>,
pub final_semicolon: bool, pub final_semicolon: bool,
} }
impl Parsable for Body { impl Parsable for Body {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> { fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut exprs = Vec::new(); let mut items = Vec::new();
fn at_end(ctx: &mut ParseCtx) -> bool { fn at_end(ctx: &mut ParseCtx) -> bool {
ctx.peek().is_none_or(|t| *t == Token::CloseCurly) ctx.peek().is_none_or(|t| *t == Token::CloseCurly)
} }
@@ -15,14 +15,19 @@ impl Parsable for Body {
if at_end(ctx) { if at_end(ctx) {
break true; break true;
} }
exprs.push(ctx.parse()?); let item: Id<Item> = ctx.parse()?;
let needs_semicolon = item.needs_semicolon(&ctx.nodes);
items.push(item);
if at_end(ctx) { if at_end(ctx) {
break false; break false;
} }
ctx.expect(Token::Semicolon)?; if needs_semicolon {
ctx.expect(Token::Semicolon)?;
}
while ctx.next_if(Token::Semicolon) {}
}; };
Ok(Self { Ok(Self {
exprs, items,
final_semicolon, final_semicolon,
}) })
} }
@@ -31,15 +36,27 @@ impl Parsable for Body {
impl FmtNode for Body { impl FmtNode for Body {
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
// surely there's a better way to do this // 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 { for &i in rest {
writeln!(f, "{}{};", " ".repeat(ctx.indent), i.dsp(ctx))?; writeln!(
} f,
if self.final_semicolon { "{}{}{}",
writeln!(f, "{}{};", " ".repeat(ctx.indent), last.dsp(ctx))?; " ".repeat(ctx.indent),
} else { i.dsp(ctx),
writeln!(f, "{}{}", " ".repeat(ctx.indent), last.dsp(ctx))?; if i.needs_semicolon(ctx.nodes) {
";"
} else {
""
}
)?;
} }
writeln!(
f,
"{}{}{}",
" ".repeat(ctx.indent),
last.dsp(ctx),
if self.final_semicolon { ";" } else { "" }
)?;
} }
Ok(()) Ok(())
} }
+27 -37
View File
@@ -16,11 +16,6 @@ pub enum Expr {
target: Id<Expr>, target: Id<Expr>,
val: Id<Expr>, val: Id<Expr>,
}, },
Define {
target: Id<Expr>,
ty: Option<Id<Type>>,
val: Id<Expr>,
},
If { If {
cond: Id<Expr>, cond: Id<Expr>,
body: Id<Expr>, body: Id<Expr>,
@@ -45,16 +40,6 @@ impl Parsable for Expr {
let val = ctx.parse_with(Self::unit)?; let val = ctx.parse_with(Self::unit)?;
Expr::Assign { target, val } 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 => { Token::OpenParen => {
let target = ctx.push_adv(res); let target = ctx.push_adv(res);
let args = ctx.list(Token::Comma, Token::CloseParen)?; let args = ctx.list(Token::Comma, Token::CloseParen)?;
@@ -76,12 +61,12 @@ impl Expr {
Token::Fn => Self::Fn(ctx.parse()?), Token::Fn => Self::Fn(ctx.parse()?),
Token::If => { Token::If => {
let cond = ctx.parse()?; let cond = ctx.parse()?;
let body = cond_body(cond, ctx)?; let body = cond_body(ctx)?;
Self::If { cond, body } Self::If { cond, body }
} }
Token::While => { Token::While => {
let cond = ctx.parse()?; let cond = ctx.parse()?;
let body = cond_body(cond, ctx)?; let body = cond_body(ctx)?;
Self::While { cond, body } Self::While { cond, body }
} }
Token::Loop => { Token::Loop => {
@@ -110,6 +95,10 @@ impl Expr {
matches!(self, Expr::Group(_)) matches!(self, Expr::Group(_))
} }
pub fn is_block(&self) -> bool {
matches!(self, Expr::Block(_))
}
pub fn block(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> { pub fn block(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> {
ctx.expect(Token::OpenCurly)?; ctx.expect(Token::OpenCurly)?;
let id = ctx.parse()?; let id = ctx.parse()?;
@@ -118,16 +107,32 @@ impl Expr {
} }
} }
fn cond_body(cond: Id<Expr>, ctx: &mut ParseCtx) -> Result<Id<Expr>, CompilerMsg> { fn cond_body(ctx: &mut ParseCtx) -> Result<Id<Expr>, CompilerMsg> {
if ctx[cond].is_group() { if ctx.next_if(Token::Do) {
ctx.parse() ctx.parse()
} else { } else {
ctx.parse_with(Expr::block) ctx.parse_with(Expr::block)
} }
} }
impl Id<Expr> {
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 { impl FmtNode for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result {
let do_ = |id: Id<Expr>| if ctx.nodes[id].is_block() { "" } else { "do " };
match *self { match *self {
Self::Ident(id) => id.fmt(f, ctx), Self::Ident(id) => id.fmt(f, ctx),
Self::Group(id) => write!(f, "({})", id.dsp(ctx)), Self::Group(id) => write!(f, "({})", id.dsp(ctx)),
@@ -142,27 +147,18 @@ impl FmtNode for Expr {
Self::Assign { target, val } => { Self::Assign { target, val } => {
write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx)) 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 } => { 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 } => { 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 } => { Self::Loop { body } => {
write!(f, "loop {}", body.dsp(ctx)) write!(f, "loop {}", body.dsp(ctx))
} }
Self::Block(body) => { Self::Block(body) => {
write!(f, "{{")?; write!(f, "{{")?;
if !ctx.nodes[body].exprs.is_empty() { if !ctx.nodes[body].items.is_empty() {
writeln!(f)?; writeln!(f)?;
ctx.indent += 3; ctx.indent += 3;
body.fmt(f, ctx)?; 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}")
}
}
+6
View File
@@ -40,3 +40,9 @@ impl FmtNode for Func {
Ok(()) Ok(())
} }
} }
impl Id<Func> {
pub fn ends_with_block(&self, nodes: &Nodes) -> bool {
nodes[self].body.ends_with_block(nodes)
}
}
+58
View File
@@ -0,0 +1,58 @@
use super::*;
pub enum Item {
Let {
name: Id<Ident>,
ty: Option<Id<Type>>,
val: Id<Expr>,
},
Struct(Id<Struct>),
Expr(Id<Expr>),
}
impl Parsable for Item {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
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<Item> {
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)
}
}
+6
View File
@@ -2,13 +2,17 @@ mod body;
mod expr; mod expr;
mod func; mod func;
mod ident; mod ident;
mod item;
mod param; mod param;
mod struct_;
mod ty; mod ty;
pub use body::*; pub use body::*;
pub use expr::*; pub use expr::*;
pub use func::*; pub use func::*;
pub use ident::*; pub use ident::*;
pub use item::*;
pub use param::*; pub use param::*;
pub use struct_::*;
pub use ty::*; pub use ty::*;
use super::{DisplayCtx, FmtNode, Id, Lit, Node, NodeVec, Parsable, ParseCtx, Token}; use super::{DisplayCtx, FmtNode, Id, Lit, Node, NodeVec, Parsable, ParseCtx, Token};
@@ -22,6 +26,8 @@ def_nodes! {
types: Type, types: Type,
funcs: Func, funcs: Func,
params: Param, params: Param,
items: Item,
structs: Struct,
} }
macro_rules! def_nodes { macro_rules! def_nodes {
+14
View File
@@ -0,0 +1,14 @@
use super::*;
pub struct Struct {
name: String,
fields: Vec<Field>,
}
impl Parsable for Struct {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
}
}
pub struct Field {
}
+6 -2
View File
@@ -1,7 +1,11 @@
x :i32 = 3; let x: i32 = 3;
while true { while true {
print("hello"); print("hello");
print(x); print(x);
}; };
y := fn(x: i32) -> i32 {x}; if x {
print("hello");
}
let y = fn(x: i32) -> i32 {x};