diff --git a/src/io/mod.rs b/src/io/mod.rs index 35ae6d3..ea80b99 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -79,7 +79,6 @@ impl Span { found = false; } } - let start_chars = text[self.start..].lines().next().unwrap().len(); let underline = "\x1b[4:3m"; let underline_color = "\x1b[58;5;1m"; let end = "\x1b[0m"; diff --git a/src/main.rs b/src/main.rs index 94bea59..7902060 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,7 @@ fn main() { let mut output = CompilerOutput::new(); let nodes = Nodes::parse_root(&path, &mut output); if let Some((nodes, root)) = nodes { - nodes.format(&mut std::io::stdout(), root).unwrap(); - println!(); + println!("{}", root.dsp(&nodes)); } output.write(&mut std::io::stdout()); } diff --git a/src/parser/cursor/lit.rs b/src/parser/cursor/lit.rs new file mode 100644 index 0000000..fc424e8 --- /dev/null +++ b/src/parser/cursor/lit.rs @@ -0,0 +1,25 @@ +use super::Token; + +#[derive(PartialEq)] +pub enum Lit { + Number(String), + Bool(bool), + String(String), +} + +impl From for Token { + fn from(value: Lit) -> Self { + Self::Lit(value) + } +} + +impl std::fmt::Display for Lit { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Lit::Number(n) => write!(f, "{n}"), + Lit::Bool(b) => write!(f, "{b}"), + Lit::String(s) => write!(f, "{s}"), + } + } +} + diff --git a/src/parser/cursor/mod.rs b/src/parser/cursor/mod.rs index e7d03df..f2c815c 100644 --- a/src/parser/cursor/mod.rs +++ b/src/parser/cursor/mod.rs @@ -1,8 +1,10 @@ use crate::io::{CompilerMsg, Span, Spanned}; use std::iter::Peekable; -pub use token::*; +mod lit; mod token; +pub use lit::*; +pub use token::*; pub struct Cursor<'a> { pub span: Span, @@ -33,10 +35,11 @@ impl<'a> Cursor<'a> { } pub fn expect_next(&mut self) -> Result { - self.next().ok_or_else(|| CompilerMsg { - spans: Vec::new(), - msg: "unexpected end of file".to_string(), - }) + self.next().ok_or_else(CompilerMsg::unexpected_eof) + } + + pub fn expect_peek(&mut self) -> Result<&Token, CompilerMsg> { + self.peek().ok_or_else(CompilerMsg::unexpected_eof) } pub fn expect(&mut self, token: Token) -> Result { @@ -44,16 +47,7 @@ impl<'a> Cursor<'a> { if next == token { Ok(next) } else { - self.unexpected(next, &format!("{token:?}")) - } - } - - pub fn expect_ident(&mut self) -> Result { - let next = self.expect_next()?; - if let Token::Ident(s) = next { - Ok(s) - } else { - self.unexpected(next, "identifier") + self.unexpected(next, &format!("'{token}'")) } } @@ -87,4 +81,11 @@ impl CompilerMsg { msg: format!("Unexpected token '{}'; expected {expected}", inst.inner), } } + + pub fn unexpected_eof() -> Self { + Self { + spans: Vec::new(), + msg: "unexpected end of file".to_string(), + } + } } diff --git a/src/parser/cursor/token.rs b/src/parser/cursor/token.rs index c747836..7e1ae6a 100644 --- a/src/parser/cursor/token.rs +++ b/src/parser/cursor/token.rs @@ -1,38 +1,31 @@ -use super::{Span, Spanned}; +use super::{Lit, Span, Spanned}; use std::{iter::Peekable, str::CharIndices}; -#[derive(PartialEq)] -pub enum Token { - // symbols - Equal, - Colon, - Semicolon, - OpenCurly, - CloseCurly, - OpenParen, - CloseParen, - Dash, - Plus, - Asterisk, - Slash, - // keywords - Let, - Fn, - // other - Ident(String), - Lit(Lit), -} - -#[derive(PartialEq)] -pub enum Lit { - Number(String), - Bool(bool), - String(String), -} - -impl From for Token { - fn from(value: Lit) -> Self { - Self::Lit(value) +def_tokens! { + symbol { + '=' => Equal, + ':' => Colon, + ';' => Semicolon, + '{' => OpenCurly, + '}' => CloseCurly, + '(' => OpenParen, + ')' => CloseParen, + '-' => Dash, + '+' => Plus, + '*' => Asterisk, + '/' => Slash, + } + keyword { + Let: "let", + Fn: "fn", + If: "if", + Loop: "loop", + While: "while", + For: "for", + } + other { + Ident(String), + Lit(Lit), } } @@ -40,14 +33,14 @@ pub type TokenInst = Spanned; pub struct Tokens<'a> { file: usize, - text: Peekable>, + chars: Peekable>, } impl<'a> Tokens<'a> { pub fn new(code: &'a str, file: usize) -> Self { Self { file, - text: code.char_indices().peekable(), + chars: code.char_indices().peekable(), } } } @@ -56,112 +49,113 @@ impl Iterator for Tokens<'_> { type Item = Spanned; fn next(&mut self) -> Option { - let (i, c) = self.text.next()?; + let (i, c) = self.chars.next()?; let mut span = Span { start: i, end: i, file: self.file, }; - Some(Spanned { - inner: match c { - '=' => Token::Equal, - '{' => Token::OpenCurly, - '}' => Token::CloseCurly, - '(' => Token::OpenParen, - ')' => Token::CloseParen, - ':' => Token::Colon, - ';' => Token::Semicolon, - '-' => Token::Dash, - '+' => Token::Plus, - '*' => Token::Asterisk, - '/' => Token::Slash, - '\n' | ' ' => { - return self.next(); - } - '0'..='9' => { - let mut s = c.to_string(); - while let Some((i, c)) = self.text.peek() - && c.is_alphanumeric() - { - s.push(*c); - span.end = *i; - self.text.next(); - } - Lit::Number(s).into() - } - '"' => { - let mut s = c.to_string(); - while let Some((i, c)) = self.text.peek() - && !matches!(c, '"') - { - s.push(*c); - span.end = *i; - self.text.next(); - } - self.text.next(); - Lit::String(s).into() - } - _ => { - let mut s = c.to_string(); - while let Some((i, c)) = self.text.peek() - && !matches!( - c, - '=' | '{' | '}' | '(' | ')' | ':' | ';' | '\n' | '"' | ' ' - ) - { - s.push(*c); - span.end = *i; - self.text.next(); - } - match s.as_str() { - "let" => Token::Let, - "fn" => Token::Fn, - "true" => Lit::Bool(true).into(), - "false" => Lit::Bool(false).into(), - _ => Token::Ident(s), - } - } - }, - span, - }) - } -} - -impl std::fmt::Display for Token { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Token::Equal => write!(f, "="), - Token::Colon => write!(f, ":"), - Token::Plus => write!(f, "+"), - Token::Dash => write!(f, "-"), - Token::Asterisk => write!(f, "*"), - Token::Slash => write!(f, "/"), - Token::Semicolon => write!(f, ";"), - Token::OpenCurly => write!(f, "{{"), - Token::CloseCurly => write!(f, "}}"), - Token::OpenParen => write!(f, "("), - Token::CloseParen => write!(f, ")"), - Token::Let => write!(f, "let"), - Token::Fn => write!(f, "fn"), - Token::Ident(s) => write!(f, "{s}"), - Token::Lit(lit) => write!(f, "{lit}"), - }?; - Ok(()) - } -} - -impl std::fmt::Display for Lit { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Lit::Number(n) => write!(f, "{n}"), - Lit::Bool(b) => write!(f, "{b}"), - Lit::String(s) => write!(f, "{s}"), + if let Some(inner) = from_char(c) { + return Some(Spanned { inner, span }); } + if c.is_whitespace() { + return self.next(); + } + let inner = match c { + '0'..='9' => { + let mut s = c.to_string(); + while let Some((i, c)) = self.chars.peek() + && c.is_alphanumeric() + { + s.push(*c); + span.end = *i; + self.chars.next(); + } + Lit::Number(s).into() + } + '"' => { + let mut s = c.to_string(); + while let Some((i, c)) = self.chars.peek() + && !matches!(c, '"') + { + s.push(*c); + span.end = *i; + self.chars.next(); + } + self.chars.next(); + Lit::String(s).into() + } + _ => { + let mut s = c.to_string(); + while let Some((i, c)) = self.chars.peek() + && c.is_alphanumeric() + { + s.push(*c); + span.end = *i; + self.chars.next(); + } + match s.as_str() { + "true" => Lit::Bool(true).into(), + "false" => Lit::Bool(false).into(), + _ => from_str(s), + } + } + }; + Some(Spanned { inner, span }) } } -impl std::fmt::Debug for Token { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "'{self}'") - } +macro_rules! expand_sym_names { + ({ + $($sym_char:expr => $sym_name:ident,)* + }) => { + $($sym_name,)* + }; + ({ + $($sym_char:expr => $sym_res:tt,)* + }) => { + expand_sym_names!($sym_res) + }; } +use expand_sym_names; + +macro_rules! def_tokens { + { + symbol $syms:tt + keyword { + $($kw_name:ident: $kw_str:expr,)* + } + other { + $($other_name:ident($data:ty),)* + } + } => { + #[derive(PartialEq)] + pub enum Token { + $($syms,)* + $($kw_name,)* + $($other_name($data),)* + } + fn from_char(c: char) -> Option { + match c { + $($sym_char => Some(Token::$sym_res),)* + _ => None, + } + } + fn from_str(s: String) -> Token { + match s.as_str() { + $($kw_str => Token::$kw_name,)* + _ => Token::Ident(s), + } + } + impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + $(Token::$sym_res => write!(f, "{}", $sym_char),)* + $(Token::$kw_name => write!(f, $kw_str),)* + $(Token::$other_name(v) => write!(f, "{v}"),)* + } + } + } + }; +} +use def_tokens; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2f154a1..a3565f6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,7 @@ mod cursor; mod node; +mod nodes; +use cursor::*; pub use node::*; +pub use nodes::*; diff --git a/src/parser/node/expr.rs b/src/parser/node/expr.rs deleted file mode 100644 index 6468ca4..0000000 --- a/src/parser/node/expr.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::{ - io::CompilerMsg, - parser::{ - cursor::{Lit, Token}, - *, - }, -}; - -pub enum Statement { - Let(Id, Id), -} - -pub enum Expr { - Ident(Id), - Lit(Id), - Negate(Id), - Assign(Id, Id), -} - -pub struct Ident { - pub inner: String, -} - -impl FmtNode for Ident { - fn format(&self, w: &mut impl std::io::Write, _: &Nodes) -> std::io::Result<()> { - write!(w, "{}", self.inner) - } -} - -impl Parsable for Expr { - fn parse(ctx: &mut ParseCtx) -> Result { - let e1 = match ctx.cursor.expect_next()? { - Token::Dash => Self::Negate(ctx.parse()?), - Token::Ident(s) => Self::Ident(ctx.ident(s)), - Token::Lit(l) => Self::Lit(ctx.lit(l)), - other => return ctx.cursor.unexpected(other, "an expression"), - }; - let Some(next) = ctx.cursor.peek() else { - return Ok(e1); - }; - Ok(match next { - Token::Equal => { - let e1 = ctx.push_adv(e1); - let e2: Id = ctx.parse()?; - Expr::Assign(e1, e2) - } - _ => e1, - }) - } -} - -impl FmtNode for Expr { - fn format(&self, w: &mut impl std::io::Write, nodes: &Nodes) -> std::io::Result<()> { - match *self { - Expr::Ident(id) => nodes.format(w, id), - Expr::Lit(id) => nodes.format(w, id), - Expr::Negate(id) => { - write!(w, "-")?; - nodes.format(w, id) - } - Expr::Assign(id1, id2) => { - nodes.format(w, id1)?; - write!(w, " = ")?; - nodes.format(w, id2) - } - } - } -} - -impl FmtNode for Lit { - fn format(&self, w: &mut impl std::io::Write, _: &Nodes) -> std::io::Result<()> { - match self { - Lit::Number(v) => write!(w, "{v}"), - Lit::Bool(v) => write!(w, "{v}"), - Lit::String(v) => write!(w, "{v}"), - } - } -} diff --git a/src/parser/node/id.rs b/src/parser/node/id.rs new file mode 100644 index 0000000..e160fc8 --- /dev/null +++ b/src/parser/node/id.rs @@ -0,0 +1,94 @@ +use crate::parser::{Node, Nodes}; +use std::marker::PhantomData; + +pub struct Id { + id: usize, + _pd: PhantomData, +} + +impl Clone for Id { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Id {} + +impl std::ops::Index> for Nodes { + type Output = N; + + fn index(&self, index: Id) -> &Self::Output { + &N::vec(self).vec[index.id] + } +} + +impl std::ops::Index<&Id> for Nodes { + type Output = N; + + fn index(&self, index: &Id) -> &Self::Output { + &N::vec(self).vec[index.id] + } +} + +#[derive(Clone, Copy)] +pub struct DisplayCtx<'a> { + pub nodes: &'a Nodes, + pub indent: usize, +} + +pub struct IdDisplay<'a, N> { + id: Id, + nodes: &'a Nodes, + indent: usize, +} + +pub trait FmtNode: Node { + fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result; +} + +impl std::fmt::Display for IdDisplay<'_, N> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.nodes[self.id].fmt( + f, + DisplayCtx { + nodes: self.nodes, + indent: self.indent, + }, + ) + } +} + +impl Id { + pub(super) fn new(id: usize) -> Self { + Self { + id, + _pd: PhantomData, + } + } +} + +impl Id { + pub fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { + ctx.nodes[*self].fmt(f, ctx) + } + pub fn dsp<'a, 'b>(&self, ctx: impl Into>) -> IdDisplay<'b, N> + where + 'a: 'b, + { + let ctx = ctx.into(); + IdDisplay { + id: *self, + nodes: ctx.nodes, + indent: ctx.indent, + } + } +} + +impl<'a> From<&'a Nodes> for DisplayCtx<'a> { + fn from(node: &'a Nodes) -> Self { + Self { + nodes: node, + indent: 0, + } + } +} diff --git a/src/parser/node/mod.rs b/src/parser/node/mod.rs index 66e24eb..002ece4 100644 --- a/src/parser/node/mod.rs +++ b/src/parser/node/mod.rs @@ -1,31 +1,23 @@ use crate::{ - io::{CompilerMsg, CompilerOutput, Span}, - parser::cursor::{Cursor, Lit}, + io::{CompilerOutput, Span}, + parser::{Cursor, Lit, nodes::*}, }; -use std::{marker::PhantomData, ops::Index}; -mod expr; -pub use expr::*; +mod id; +mod parse; +pub use id::*; +pub use parse::*; -pub trait Parsable: Sized + Node { - fn parse(ctx: &mut ParseCtx) -> Result; -} - -pub struct ParseCtx<'a> { - start: usize, - cursor: Cursor<'a>, - nodes: Nodes, -} - -def_nodes!( +def_nodes! { exprs: Expr, idents: Ident, statements: Statement, + blocks: Block, lits: Lit, -); +} impl Nodes { - pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<(Self, Id)> { + pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<(Self, Id)> { let root_code = match std::fs::read_to_string(path) { Ok(code) => code, Err(err) => { @@ -34,44 +26,15 @@ impl Nodes { } }; output.files.push(path.to_string()); - let nodes = Self::default(); - let mut ctx = ParseCtx { - start: 0, - nodes, - cursor: Cursor::new(&root_code, 0), - }; - let root = match ctx.parse::() { + let mut ctx = ParseCtx::new(Cursor::new(&root_code, 0)); + let root = match ctx.parse() { Ok(expr) => expr, Err(msg) => { output.error(msg); return None; } }; - Some((ctx.nodes, root)) - } - - pub fn format( - &self, - w: &mut impl std::io::Write, - id: Id, - ) -> std::io::Result<()> { - self[id].format(w, self) - } -} - -impl Index> for Nodes { - type Output = N; - - fn index(&self, index: Id) -> &Self::Output { - &N::vec(self).vec[index.id] - } -} - -impl Index<&Id> for Nodes { - type Output = N; - - fn index(&self, index: &Id) -> &Self::Output { - &N::vec(self).vec[index.id] + Some((ctx.finish(), root)) } } @@ -86,10 +49,7 @@ impl NodeVec { let id = self.vec.len(); self.vec.push(v); self.spans.push(span); - Id { - id, - _pd: PhantomData, - } + Id::new(id) } } @@ -102,61 +62,13 @@ impl Default for NodeVec { } } -pub struct Id { - id: usize, - _pd: PhantomData, -} - -impl Clone for Id { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for Id {} - pub trait Node: Sized { fn vec(nodes: &Nodes) -> &NodeVec; fn vec_mut(nodes: &mut Nodes) -> &mut NodeVec; } -impl ParseCtx<'_> { - pub fn parse(&mut self) -> Result, CompilerMsg> { - let old_start = self.start; - self.start = self.cursor.peek_start(); - let res = P::parse(self).map(|r| self.push(r)); - self.start = old_start; - res - } - - pub fn ident(&mut self, s: String) -> Id { - let span = self.cursor.span; - self.nodes.idents.add(Ident { inner: s }, span) - } - pub fn lit(&mut self, lit: Lit) -> Id { - let span = self.cursor.span; - self.nodes.lits.add(lit, span) - } - pub fn push_adv(&mut self, node: N) -> Id { - let res = self.push(node); - self.cursor.next(); - res - } - pub fn push(&mut self, node: N) -> Id { - let end = self.cursor.cur_end(); - N::vec_mut(&mut self.nodes).add( - node, - Span { - file: self.cursor.file(), - start: self.start, - end, - }, - ) - } -} - macro_rules! def_nodes { - ($($field:ident: $ty:ident,)*) => { + {$($field:ident: $ty:ident,)*} => { #[derive(Default)] pub struct Nodes { $($field: NodeVec<$ty>,)* @@ -173,7 +85,3 @@ macro_rules! def_nodes { }; } use def_nodes; - -pub trait FmtNode: Node { - fn format(&self, w: &mut impl std::io::Write, nodes: &Nodes) -> std::io::Result<()>; -} diff --git a/src/parser/node/parse.rs b/src/parser/node/parse.rs new file mode 100644 index 0000000..7a9ef38 --- /dev/null +++ b/src/parser/node/parse.rs @@ -0,0 +1,76 @@ +use crate::{ + io::{CompilerMsg, Span}, + parser::{ + Id, Ident, Node, Nodes, + cursor::{Cursor, Lit}, + }, +}; + +pub trait Parsable: Sized + Node { + fn parse(ctx: &mut ParseCtx) -> Result; +} + +pub struct ParseCtx<'a> { + start: usize, + cursor: Cursor<'a>, + nodes: Nodes, +} + +impl<'a> ParseCtx<'a> { + pub fn new(cursor: Cursor<'a>) -> Self { + Self { + start: 0, + nodes: Nodes::default(), + cursor, + } + } + pub fn parse(&mut self) -> Result, CompilerMsg> { + let old_start = self.start; + self.start = self.cursor.peek_start(); + let res = P::parse(self).map(|r| self.push(r)); + self.start = old_start; + res + } + + pub fn ident(&mut self, s: String) -> Id { + let span = self.cursor.span; + self.nodes.idents.add(Ident { inner: s }, span) + } + pub fn lit(&mut self, lit: Lit) -> Id { + let span = self.cursor.span; + self.nodes.lits.add(lit, span) + } + pub fn push_adv(&mut self, node: N) -> Id { + let res = self.push(node); + self.cursor.next(); + res + } + pub fn push(&mut self, node: N) -> Id { + let end = self.cursor.cur_end(); + N::vec_mut(&mut self.nodes).add( + node, + Span { + file: self.cursor.file(), + start: self.start, + end, + }, + ) + } + pub fn finish(self) -> Nodes { + self.nodes + } +} + +impl<'a> std::ops::Deref for ParseCtx<'a> { + type Target = Cursor<'a>; + + fn deref(&self) -> &Self::Target { + &self.cursor + } +} + +impl<'a> std::ops::DerefMut for ParseCtx<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cursor + } +} diff --git a/src/parser/nodes/block.rs b/src/parser/nodes/block.rs new file mode 100644 index 0000000..90297f8 --- /dev/null +++ b/src/parser/nodes/block.rs @@ -0,0 +1,34 @@ +use super::*; + +pub struct Block { + statements: Vec>, +} + +impl Parsable for Block { + fn parse(ctx: &mut ParseCtx) -> Result { + let mut statements = Vec::new(); + if *ctx.expect_peek()? != Token::CloseCurly { + statements.push(ctx.parse()?); + while *ctx.expect_peek()? == Token::Semicolon { + ctx.next(); + statements.push(ctx.parse()?); + } + } + Ok(Self { statements }) + } +} + +impl FmtNode for Block { + fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result { + ctx.indent += 3; + write!(f, "{{")?; + if !self.statements.is_empty() { + writeln!(f)?; + } + for &s in &self.statements { + writeln!(f, "{}{};", " ".repeat(ctx.indent), s.dsp(ctx))?; + } + write!(f, "}}")?; + Ok(()) + } +} diff --git a/src/parser/nodes/expr.rs b/src/parser/nodes/expr.rs new file mode 100644 index 0000000..8bea327 --- /dev/null +++ b/src/parser/nodes/expr.rs @@ -0,0 +1,55 @@ +pub use super::*; + +pub enum Expr { + Ident(Id), + Lit(Id), + Negate(Id), + Assign(Id, Id), +} + +impl Parsable for Expr { + fn parse(ctx: &mut ParseCtx) -> Result { + let e1 = match ctx.expect_next()? { + Token::Dash => Self::Negate(ctx.parse()?), + Token::Ident(s) => Self::Ident(ctx.ident(s)), + Token::Lit(l) => Self::Lit(ctx.lit(l)), + other => return ctx.unexpected(other, "an expression"), + }; + let Some(next) = ctx.peek() else { + return Ok(e1); + }; + Ok(match next { + Token::Equal => { + let e1 = ctx.push_adv(e1); + let e2: Id = ctx.parse()?; + Expr::Assign(e1, e2) + } + _ => e1, + }) + } +} + +impl FmtNode for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { + match *self { + Expr::Ident(id) => id.fmt(f, ctx), + Expr::Lit(id) => id.fmt(f, ctx), + Expr::Negate(id) => { + write!(f, "-{}", id.dsp(ctx)) + } + Expr::Assign(id1, id2) => { + write!(f, "{} = {}", id1.dsp(ctx), id2.dsp(ctx)) + } + } + } +} + +impl FmtNode for Lit { + fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result { + match self { + Lit::Number(v) => write!(f, "{v}"), + Lit::Bool(v) => write!(f, "{v}"), + Lit::String(v) => write!(f, "{v}"), + } + } +} diff --git a/src/parser/nodes/ident.rs b/src/parser/nodes/ident.rs new file mode 100644 index 0000000..37dc936 --- /dev/null +++ b/src/parser/nodes/ident.rs @@ -0,0 +1,20 @@ +use super::*; + +pub struct Ident { + pub inner: String, +} + +impl FmtNode for Ident { + fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result { + write!(f, "{}", self.inner) + } +} + +impl Parsable for Ident { + fn parse(ctx: &mut super::ParseCtx) -> Result { + match ctx.expect_next()? { + Token::Ident(ident) => Ok(Self { inner: ident }), + t => ctx.unexpected(t, "an identifier"), + } + } +} diff --git a/src/parser/nodes/mod.rs b/src/parser/nodes/mod.rs new file mode 100644 index 0000000..55d8f88 --- /dev/null +++ b/src/parser/nodes/mod.rs @@ -0,0 +1,11 @@ +mod block; +mod expr; +mod ident; +mod statement; +pub use block::*; +pub use expr::*; +pub use ident::*; +pub use statement::*; + +use super::{DisplayCtx, FmtNode, Id, Lit, Parsable, ParseCtx, Token}; +use crate::io::CompilerMsg; diff --git a/src/parser/nodes/statement.rs b/src/parser/nodes/statement.rs new file mode 100644 index 0000000..4fa4e54 --- /dev/null +++ b/src/parser/nodes/statement.rs @@ -0,0 +1,41 @@ +pub use super::*; + +pub enum Statement { + Let(Id, Id), + If { cond: Id, body: Id }, + Expr(Id), +} + +impl Parsable for Statement { + fn parse(ctx: &mut ParseCtx) -> Result { + Ok(match ctx.expect_peek()? { + Token::Let => { + ctx.next(); + let name = ctx.parse()?; + ctx.expect(Token::Equal)?; + Self::Let(name, ctx.parse()?) + } + Token::If => { + ctx.next(); + let cond = ctx.parse()?; + let body = ctx.parse()?; + Self::If { cond, body } + } + _ => Self::Expr(ctx.parse()?), + }) + } +} + +impl FmtNode for Statement { + fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { + match *self { + Self::If { cond, body } => { + write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx)) + } + Self::Let(name, expr) => { + write!(f, "let {} = {}", name.dsp(ctx), expr.dsp(ctx)) + } + Self::Expr(expr) => expr.fmt(f, ctx), + } + } +} diff --git a/test/main.lang b/test/main.lang index f0974a1..29fabab 100644 --- a/test/main.lang +++ b/test/main.lang @@ -1 +1 @@ -x =/ arst -3 +let x = arst -3