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;
}
}
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";
+1 -2
View File
@@ -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());
}
+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 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<Token, CompilerMsg> {
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<Token, CompilerMsg> {
@@ -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<String, CompilerMsg> {
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(),
}
}
}
+128 -134
View File
@@ -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<Lit> 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<Token>;
pub struct Tokens<'a> {
file: usize,
text: Peekable<CharIndices<'a>>,
chars: Peekable<CharIndices<'a>>,
}
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<Token>;
fn next(&mut self) -> Option<Self::Item> {
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<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),
}
}
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;
+3
View File
@@ -1,4 +1,7 @@
mod cursor;
mod node;
mod nodes;
use cursor::*;
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::{
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<Self, CompilerMsg>;
}
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<Expr>)> {
pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<(Self, Id<Block>)> {
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::<Expr>() {
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<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]
Some((ctx.finish(), root))
}
}
@@ -86,10 +49,7 @@ impl<N> NodeVec<N> {
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<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 {
fn vec(nodes: &Nodes) -> &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 {
($($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<()>;
}
+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