This commit is contained in:
2026-04-10 16:13:45 -04:00
parent bdf08ce52c
commit 29316e6353
16 changed files with 520 additions and 338 deletions
-1
View File
@@ -79,7 +79,6 @@ impl Span {
found = false; found = false;
} }
} }
let start_chars = text[self.start..].lines().next().unwrap().len();
let underline = "\x1b[4:3m"; let underline = "\x1b[4:3m";
let underline_color = "\x1b[58;5;1m"; let underline_color = "\x1b[58;5;1m";
let end = "\x1b[0m"; let end = "\x1b[0m";
+1 -2
View File
@@ -12,8 +12,7 @@ fn main() {
let mut output = CompilerOutput::new(); let mut output = CompilerOutput::new();
let nodes = Nodes::parse_root(&path, &mut output); let nodes = Nodes::parse_root(&path, &mut output);
if let Some((nodes, root)) = nodes { if let Some((nodes, root)) = nodes {
nodes.format(&mut std::io::stdout(), root).unwrap(); println!("{}", root.dsp(&nodes));
println!();
} }
output.write(&mut std::io::stdout()); output.write(&mut std::io::stdout());
} }
+25
View File
@@ -0,0 +1,25 @@
use super::Token;
#[derive(PartialEq)]
pub enum Lit {
Number(String),
Bool(bool),
String(String),
}
impl From<Lit> 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}"),
}
}
}
+16 -15
View File
@@ -1,8 +1,10 @@
use crate::io::{CompilerMsg, Span, Spanned}; use crate::io::{CompilerMsg, Span, Spanned};
use std::iter::Peekable; use std::iter::Peekable;
pub use token::*;
mod lit;
mod token; mod token;
pub use lit::*;
pub use token::*;
pub struct Cursor<'a> { pub struct Cursor<'a> {
pub span: Span, pub span: Span,
@@ -33,10 +35,11 @@ impl<'a> Cursor<'a> {
} }
pub fn expect_next(&mut self) -> Result<Token, CompilerMsg> { pub fn expect_next(&mut self) -> Result<Token, CompilerMsg> {
self.next().ok_or_else(|| CompilerMsg { self.next().ok_or_else(CompilerMsg::unexpected_eof)
spans: Vec::new(), }
msg: "unexpected end of file".to_string(),
}) 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<Token, CompilerMsg> { pub fn expect(&mut self, token: Token) -> Result<Token, CompilerMsg> {
@@ -44,16 +47,7 @@ impl<'a> Cursor<'a> {
if next == token { if next == token {
Ok(next) Ok(next)
} else { } else {
self.unexpected(next, &format!("{token:?}")) self.unexpected(next, &format!("'{token}'"))
}
}
pub fn expect_ident(&mut self) -> Result<String, CompilerMsg> {
let next = self.expect_next()?;
if let Token::Ident(s) = next {
Ok(s)
} else {
self.unexpected(next, "identifier")
} }
} }
@@ -87,4 +81,11 @@ impl CompilerMsg {
msg: format!("Unexpected token '{}'; expected {expected}", inst.inner), msg: format!("Unexpected token '{}'; expected {expected}", inst.inner),
} }
} }
pub fn unexpected_eof() -> Self {
Self {
spans: Vec::new(),
msg: "unexpected end of file".to_string(),
}
}
} }
+95 -101
View File
@@ -1,38 +1,31 @@
use super::{Span, Spanned}; use super::{Lit, Span, Spanned};
use std::{iter::Peekable, str::CharIndices}; use std::{iter::Peekable, str::CharIndices};
#[derive(PartialEq)] def_tokens! {
pub enum Token { symbol {
// symbols '=' => Equal,
Equal, ':' => Colon,
Colon, ';' => Semicolon,
Semicolon, '{' => OpenCurly,
OpenCurly, '}' => CloseCurly,
CloseCurly, '(' => OpenParen,
OpenParen, ')' => CloseParen,
CloseParen, '-' => Dash,
Dash, '+' => Plus,
Plus, '*' => Asterisk,
Asterisk, '/' => Slash,
Slash, }
// keywords keyword {
Let, Let: "let",
Fn, Fn: "fn",
// other If: "if",
Loop: "loop",
While: "while",
For: "for",
}
other {
Ident(String), Ident(String),
Lit(Lit), Lit(Lit),
}
#[derive(PartialEq)]
pub enum Lit {
Number(String),
Bool(bool),
String(String),
}
impl From<Lit> for Token {
fn from(value: Lit) -> Self {
Self::Lit(value)
} }
} }
@@ -40,14 +33,14 @@ pub type TokenInst = Spanned<Token>;
pub struct Tokens<'a> { pub struct Tokens<'a> {
file: usize, file: usize,
text: Peekable<CharIndices<'a>>, chars: Peekable<CharIndices<'a>>,
} }
impl<'a> Tokens<'a> { impl<'a> Tokens<'a> {
pub fn new(code: &'a str, file: usize) -> Self { pub fn new(code: &'a str, file: usize) -> Self {
Self { Self {
file, file,
text: code.char_indices().peekable(), chars: code.char_indices().peekable(),
} }
} }
} }
@@ -56,112 +49,113 @@ impl Iterator for Tokens<'_> {
type Item = Spanned<Token>; type Item = Spanned<Token>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let (i, c) = self.text.next()?; let (i, c) = self.chars.next()?;
let mut span = Span { let mut span = Span {
start: i, start: i,
end: i, end: i,
file: self.file, file: self.file,
}; };
Some(Spanned { if let Some(inner) = from_char(c) {
inner: match c { return Some(Spanned { inner, span });
'=' => Token::Equal, }
'{' => Token::OpenCurly, if c.is_whitespace() {
'}' => Token::CloseCurly,
'(' => Token::OpenParen,
')' => Token::CloseParen,
':' => Token::Colon,
';' => Token::Semicolon,
'-' => Token::Dash,
'+' => Token::Plus,
'*' => Token::Asterisk,
'/' => Token::Slash,
'\n' | ' ' => {
return self.next(); return self.next();
} }
let inner = match c {
'0'..='9' => { '0'..='9' => {
let mut s = c.to_string(); let mut s = c.to_string();
while let Some((i, c)) = self.text.peek() while let Some((i, c)) = self.chars.peek()
&& c.is_alphanumeric() && c.is_alphanumeric()
{ {
s.push(*c); s.push(*c);
span.end = *i; span.end = *i;
self.text.next(); self.chars.next();
} }
Lit::Number(s).into() Lit::Number(s).into()
} }
'"' => { '"' => {
let mut s = c.to_string(); let mut s = c.to_string();
while let Some((i, c)) = self.text.peek() while let Some((i, c)) = self.chars.peek()
&& !matches!(c, '"') && !matches!(c, '"')
{ {
s.push(*c); s.push(*c);
span.end = *i; span.end = *i;
self.text.next(); self.chars.next();
} }
self.text.next(); self.chars.next();
Lit::String(s).into() Lit::String(s).into()
} }
_ => { _ => {
let mut s = c.to_string(); let mut s = c.to_string();
while let Some((i, c)) = self.text.peek() while let Some((i, c)) = self.chars.peek()
&& !matches!( && c.is_alphanumeric()
c,
'=' | '{' | '}' | '(' | ')' | ':' | ';' | '\n' | '"' | ' '
)
{ {
s.push(*c); s.push(*c);
span.end = *i; span.end = *i;
self.text.next(); self.chars.next();
} }
match s.as_str() { match s.as_str() {
"let" => Token::Let,
"fn" => Token::Fn,
"true" => Lit::Bool(true).into(), "true" => Lit::Bool(true).into(),
"false" => Lit::Bool(false).into(), "false" => Lit::Bool(false).into(),
_ => from_str(s),
}
}
};
Some(Spanned { inner, span })
}
}
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<Token> {
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), _ => Token::Ident(s),
} }
} }
}, impl std::fmt::Display for Token {
span,
})
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Token::Equal => write!(f, "="), $(Token::$sym_res => write!(f, "{}", $sym_char),)*
Token::Colon => write!(f, ":"), $(Token::$kw_name => write!(f, $kw_str),)*
Token::Plus => write!(f, "+"), $(Token::$other_name(v) => write!(f, "{v}"),)*
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}"),
}
}
}
impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "'{self}'")
} }
}
}
};
} }
use def_tokens;
+3
View File
@@ -1,4 +1,7 @@
mod cursor; mod cursor;
mod node; mod node;
mod nodes;
use cursor::*;
pub use node::*; pub use node::*;
pub use nodes::*;
-78
View File
@@ -1,78 +0,0 @@
use crate::{
io::CompilerMsg,
parser::{
cursor::{Lit, Token},
*,
},
};
pub enum Statement {
Let(Id<Ident>, Id<Expr>),
}
pub enum Expr {
Ident(Id<Ident>),
Lit(Id<Lit>),
Negate(Id<Expr>),
Assign(Id<Expr>, Id<Expr>),
}
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<Self, CompilerMsg> {
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<Expr> = 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}"),
}
}
}
+94
View File
@@ -0,0 +1,94 @@
use crate::parser::{Node, Nodes};
use std::marker::PhantomData;
pub struct Id<T> {
id: usize,
_pd: PhantomData<T>,
}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Id<T> {}
impl<N: Node> std::ops::Index<Id<N>> for Nodes {
type Output = N;
fn index(&self, index: Id<N>) -> &Self::Output {
&N::vec(self).vec[index.id]
}
}
impl<N: Node> std::ops::Index<&Id<N>> for Nodes {
type Output = N;
fn index(&self, index: &Id<N>) -> &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<N>,
nodes: &'a Nodes,
indent: usize,
}
pub trait FmtNode: Node {
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result;
}
impl<N: FmtNode> 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<N> Id<N> {
pub(super) fn new(id: usize) -> Self {
Self {
id,
_pd: PhantomData,
}
}
}
impl<N: FmtNode> Id<N> {
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<DisplayCtx<'a>>) -> 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,
}
}
}
+15 -107
View File
@@ -1,31 +1,23 @@
use crate::{ use crate::{
io::{CompilerMsg, CompilerOutput, Span}, io::{CompilerOutput, Span},
parser::cursor::{Cursor, Lit}, parser::{Cursor, Lit, nodes::*},
}; };
use std::{marker::PhantomData, ops::Index};
mod expr; mod id;
pub use expr::*; mod parse;
pub use id::*;
pub use parse::*;
pub trait Parsable: Sized + Node { def_nodes! {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg>;
}
pub struct ParseCtx<'a> {
start: usize,
cursor: Cursor<'a>,
nodes: Nodes,
}
def_nodes!(
exprs: Expr, exprs: Expr,
idents: Ident, idents: Ident,
statements: Statement, statements: Statement,
blocks: Block,
lits: Lit, lits: Lit,
); }
impl Nodes { impl Nodes {
pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<(Self, Id<Expr>)> { pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<(Self, Id<Block>)> {
let root_code = match std::fs::read_to_string(path) { let root_code = match std::fs::read_to_string(path) {
Ok(code) => code, Ok(code) => code,
Err(err) => { Err(err) => {
@@ -34,44 +26,15 @@ impl Nodes {
} }
}; };
output.files.push(path.to_string()); output.files.push(path.to_string());
let nodes = Self::default(); let mut ctx = ParseCtx::new(Cursor::new(&root_code, 0));
let mut ctx = ParseCtx { let root = match ctx.parse() {
start: 0,
nodes,
cursor: Cursor::new(&root_code, 0),
};
let root = match ctx.parse::<Expr>() {
Ok(expr) => expr, Ok(expr) => expr,
Err(msg) => { Err(msg) => {
output.error(msg); output.error(msg);
return None; return None;
} }
}; };
Some((ctx.nodes, root)) Some((ctx.finish(), root))
}
pub fn format<N: FmtNode>(
&self,
w: &mut impl std::io::Write,
id: Id<N>,
) -> std::io::Result<()> {
self[id].format(w, self)
}
}
impl<N: Node> Index<Id<N>> for Nodes {
type Output = N;
fn index(&self, index: Id<N>) -> &Self::Output {
&N::vec(self).vec[index.id]
}
}
impl<N: Node> Index<&Id<N>> for Nodes {
type Output = N;
fn index(&self, index: &Id<N>) -> &Self::Output {
&N::vec(self).vec[index.id]
} }
} }
@@ -86,10 +49,7 @@ impl<N> NodeVec<N> {
let id = self.vec.len(); let id = self.vec.len();
self.vec.push(v); self.vec.push(v);
self.spans.push(span); self.spans.push(span);
Id { Id::new(id)
id,
_pd: PhantomData,
}
} }
} }
@@ -102,61 +62,13 @@ impl<N> Default for NodeVec<N> {
} }
} }
pub struct Id<T> {
id: usize,
_pd: PhantomData<T>,
}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Id<T> {}
pub trait Node: Sized { pub trait Node: Sized {
fn vec(nodes: &Nodes) -> &NodeVec<Self>; fn vec(nodes: &Nodes) -> &NodeVec<Self>;
fn vec_mut(nodes: &mut Nodes) -> &mut NodeVec<Self>; fn vec_mut(nodes: &mut Nodes) -> &mut NodeVec<Self>;
} }
impl ParseCtx<'_> {
pub fn parse<P: Parsable>(&mut self) -> Result<Id<P>, 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<Ident> {
let span = self.cursor.span;
self.nodes.idents.add(Ident { inner: s }, span)
}
pub fn lit(&mut self, lit: Lit) -> Id<Lit> {
let span = self.cursor.span;
self.nodes.lits.add(lit, span)
}
pub fn push_adv<N: Node>(&mut self, node: N) -> Id<N> {
let res = self.push(node);
self.cursor.next();
res
}
pub fn push<N: Node>(&mut self, node: N) -> Id<N> {
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 { macro_rules! def_nodes {
($($field:ident: $ty:ident,)*) => { {$($field:ident: $ty:ident,)*} => {
#[derive(Default)] #[derive(Default)]
pub struct Nodes { pub struct Nodes {
$($field: NodeVec<$ty>,)* $($field: NodeVec<$ty>,)*
@@ -173,7 +85,3 @@ macro_rules! def_nodes {
}; };
} }
use def_nodes; use def_nodes;
pub trait FmtNode: Node {
fn format(&self, w: &mut impl std::io::Write, nodes: &Nodes) -> std::io::Result<()>;
}
+76
View File
@@ -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<Self, CompilerMsg>;
}
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<P: Parsable>(&mut self) -> Result<Id<P>, 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<Ident> {
let span = self.cursor.span;
self.nodes.idents.add(Ident { inner: s }, span)
}
pub fn lit(&mut self, lit: Lit) -> Id<Lit> {
let span = self.cursor.span;
self.nodes.lits.add(lit, span)
}
pub fn push_adv<N: Node>(&mut self, node: N) -> Id<N> {
let res = self.push(node);
self.cursor.next();
res
}
pub fn push<N: Node>(&mut self, node: N) -> Id<N> {
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
}
}
+34
View File
@@ -0,0 +1,34 @@
use super::*;
pub struct Block {
statements: Vec<Id<Statement>>,
}
impl Parsable for Block {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
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(())
}
}
+55
View File
@@ -0,0 +1,55 @@
pub use super::*;
pub enum Expr {
Ident(Id<Ident>),
Lit(Id<Lit>),
Negate(Id<Expr>),
Assign(Id<Expr>, Id<Expr>),
}
impl Parsable for Expr {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
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<Expr> = 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}"),
}
}
}
+20
View File
@@ -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<Self, crate::io::CompilerMsg> {
match ctx.expect_next()? {
Token::Ident(ident) => Ok(Self { inner: ident }),
t => ctx.unexpected(t, "an identifier"),
}
}
}
+11
View File
@@ -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;
+41
View File
@@ -0,0 +1,41 @@
pub use super::*;
pub enum Statement {
Let(Id<Ident>, Id<Expr>),
If { cond: Id<Expr>, body: Id<Expr> },
Expr(Id<Expr>),
}
impl Parsable for Statement {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
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),
}
}
}
+1 -1
View File
@@ -1 +1 @@
x =/ arst -3 let x = arst -3