work
This commit is contained in:
@@ -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
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+92
-98
@@ -1,53 +1,46 @@
|
||||
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
|
||||
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),
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Lit {
|
||||
Number(String),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl From<Lit> for Token {
|
||||
fn from(value: Lit) -> Self {
|
||||
Self::Lit(value)
|
||||
}
|
||||
}
|
||||
|
||||
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' | ' ' => {
|
||||
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.text.peek()
|
||||
while let Some((i, c)) = self.chars.peek()
|
||||
&& c.is_alphanumeric()
|
||||
{
|
||||
s.push(*c);
|
||||
span.end = *i;
|
||||
self.text.next();
|
||||
self.chars.next();
|
||||
}
|
||||
Lit::Number(s).into()
|
||||
}
|
||||
'"' => {
|
||||
let mut s = c.to_string();
|
||||
while let Some((i, c)) = self.text.peek()
|
||||
while let Some((i, c)) = self.chars.peek()
|
||||
&& !matches!(c, '"')
|
||||
{
|
||||
s.push(*c);
|
||||
span.end = *i;
|
||||
self.text.next();
|
||||
self.chars.next();
|
||||
}
|
||||
self.text.next();
|
||||
self.chars.next();
|
||||
Lit::String(s).into()
|
||||
}
|
||||
_ => {
|
||||
let mut s = c.to_string();
|
||||
while let Some((i, c)) = self.text.peek()
|
||||
&& !matches!(
|
||||
c,
|
||||
'=' | '{' | '}' | '(' | ')' | ':' | ';' | '\n' | '"' | ' '
|
||||
)
|
||||
while let Some((i, c)) = self.chars.peek()
|
||||
&& c.is_alphanumeric()
|
||||
{
|
||||
s.push(*c);
|
||||
span.end = *i;
|
||||
self.text.next();
|
||||
self.chars.next();
|
||||
}
|
||||
match s.as_str() {
|
||||
"let" => Token::Let,
|
||||
"fn" => Token::Fn,
|
||||
"true" => Lit::Bool(true).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),
|
||||
}
|
||||
}
|
||||
},
|
||||
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}"),
|
||||
$(Token::$sym_res => write!(f, "{}", $sym_char),)*
|
||||
$(Token::$kw_name => write!(f, $kw_str),)*
|
||||
$(Token::$other_name(v) => write!(f, "{v}"),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{self}'")
|
||||
}
|
||||
};
|
||||
}
|
||||
use def_tokens;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
mod cursor;
|
||||
mod node;
|
||||
mod nodes;
|
||||
|
||||
use cursor::*;
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
|
||||
@@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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<()>;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
@@ -1 +1 @@
|
||||
x =/ arst -3
|
||||
let x = arst -3
|
||||
|
||||
Reference in New Issue
Block a user