parser3
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
use crate::parser::error::ParseError;
|
||||
pub use span::*;
|
||||
use std::iter::Peekable;
|
||||
pub use token::*;
|
||||
|
||||
mod span;
|
||||
mod token;
|
||||
|
||||
pub struct Cursor<'a> {
|
||||
pub span: Span,
|
||||
tokens: Peekable<Tokens<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
pub fn new(text: &'a str) -> Self {
|
||||
Self {
|
||||
span: Span { first: 0, last: 0 },
|
||||
tokens: Tokens::new(text).peekable(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Token> {
|
||||
self.tokens.next().map(|inst| {
|
||||
self.span = inst.span;
|
||||
inst.inner
|
||||
})
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> Option<&Token> {
|
||||
self.tokens.peek().map(|inst| &inst.inner)
|
||||
}
|
||||
|
||||
pub fn expect_next(&mut self) -> Result<Token, ParseError> {
|
||||
self.next().ok_or_else(|| ParseError {
|
||||
spans: Vec::new(),
|
||||
msg: "unexpected end of file".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expect(&mut self, token: Token) -> Result<Token, ParseError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == token {
|
||||
Ok(next)
|
||||
} else {
|
||||
self.unexpected(next, &format!("{token:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_ident(&mut self) -> Result<String, ParseError> {
|
||||
let next = self.expect_next()?;
|
||||
if let Token::Ident(s) = next {
|
||||
Ok(s)
|
||||
} else {
|
||||
self.unexpected(next, "identifier")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unexpected<T>(&self, token: Token, expected: &str) -> Result<T, ParseError> {
|
||||
Err(ParseError::unexpected_token(
|
||||
Spanned {
|
||||
inner: token,
|
||||
span: self.span,
|
||||
},
|
||||
expected,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn peek_first(&mut self) -> usize {
|
||||
self.tokens.peek().map(|i| i.span.first).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn cur_last(&mut self) -> usize {
|
||||
self.span.last
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Span {
|
||||
pub first: usize,
|
||||
pub last: usize,
|
||||
}
|
||||
|
||||
pub struct Spanned<T> {
|
||||
pub inner: T,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for Spanned<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for Spanned<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
use super::{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)
|
||||
}
|
||||
}
|
||||
|
||||
pub type TokenInst = Spanned<Token>;
|
||||
|
||||
pub struct Tokens<'a> {
|
||||
text: Peekable<CharIndices<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Tokens<'a> {
|
||||
pub fn new(code: &'a str) -> Self {
|
||||
Self {
|
||||
text: code.char_indices().peekable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Tokens<'_> {
|
||||
type Item = Spanned<Token>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (i, c) = self.text.next()?;
|
||||
let mut span = Span { first: i, last: i };
|
||||
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.last = *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.last = *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.last = *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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{self}'")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use crate::parser::cursor::{Span, TokenInst};
|
||||
|
||||
pub struct ParseError {
|
||||
pub spans: Vec<Span>,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn unexpected_token(inst: TokenInst, expected: &str) -> Self {
|
||||
Self {
|
||||
spans: vec![inst.span],
|
||||
msg: format!("Unexpected token {}; expected {expected}", inst.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
// mod v1;
|
||||
// mod v2;
|
||||
mod v3;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod node;
|
||||
|
||||
pub use v3::*;
|
||||
pub fn parse(code: &str) {}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::parser::{
|
||||
cursor::{Cursor, Span, Token},
|
||||
error::ParseError,
|
||||
};
|
||||
|
||||
pub trait Parsable: Sized {
|
||||
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError>;
|
||||
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self>;
|
||||
}
|
||||
|
||||
pub struct ParseCtx<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
nodes: Nodes,
|
||||
}
|
||||
|
||||
pub struct Nodes {
|
||||
statements: NodeVec<Statement>,
|
||||
exprs: NodeVec<Expr>,
|
||||
idents: NodeVec<Ident>,
|
||||
}
|
||||
|
||||
pub struct NodeVec<N> {
|
||||
vec: Vec<N>,
|
||||
spans: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<N> NodeVec<N> {
|
||||
pub fn add(&mut self, v: N, span: Span) -> Id<N> {
|
||||
let id = self.vec.len();
|
||||
self.vec.push(v);
|
||||
self.spans.push(span);
|
||||
Id {
|
||||
id,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Let(Id<Ident>, Id<Expr>),
|
||||
}
|
||||
|
||||
pub enum Expr {
|
||||
Ident(Id<Ident>),
|
||||
Negate(Id<Expr>),
|
||||
Assign(Id<Expr>, Id<Expr>),
|
||||
}
|
||||
|
||||
pub struct Id<T> {
|
||||
id: usize,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct Ident {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl Parsable for Expr {
|
||||
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError> {
|
||||
Ok(match ctx.cursor.expect_next()? {
|
||||
Token::Dash => Self::Negate(ctx.parse()?),
|
||||
Token::Ident(s) => Self::Ident(ctx.ident(s)),
|
||||
other => return ctx.cursor.unexpected(other, "an expression"),
|
||||
})
|
||||
}
|
||||
|
||||
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self> {
|
||||
&mut nodes.exprs
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseCtx<'_> {
|
||||
pub fn parse<P: Parsable>(&mut self) -> Result<Id<P>, ParseError> {
|
||||
let first = self.cursor.peek_first();
|
||||
P::parse(self).map(|r| {
|
||||
let last = self.cursor.cur_last();
|
||||
P::vec(&mut self.nodes).add(r, Span { first, last })
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ident(&mut self, s: String) -> Id<Ident> {
|
||||
let span = self.cursor.span;
|
||||
self.nodes.idents.add(Ident { inner: s }, span)
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
use super::error::ParserError;
|
||||
use super::token::{CharCursor, Keyword, Symbol, Token, TokenInstance};
|
||||
use super::FilePos;
|
||||
|
||||
pub struct TokenCursor<'a> {
|
||||
cursor: CharCursor<'a>,
|
||||
next: Option<TokenInstance>,
|
||||
next_pos: FilePos,
|
||||
prev_end: FilePos,
|
||||
}
|
||||
|
||||
impl<'a> TokenCursor<'a> {
|
||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
||||
self.prev_end = self.cursor.prev_pos();
|
||||
self.next_pos = self.cursor.next_pos();
|
||||
std::mem::replace(&mut self.next, TokenInstance::parse(&mut self.cursor))
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<TokenInstance, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())?;
|
||||
Ok(self.next().unwrap())
|
||||
}
|
||||
pub fn expect_token(&mut self, t: Token) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if t == next.token {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::unexpected_token(&next, &format!("{t:?}")))
|
||||
}
|
||||
}
|
||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), ParserError> {
|
||||
self.expect_token(Token::Symbol(symbol))
|
||||
}
|
||||
pub fn seek_sym(&mut self, symbol: Symbol) {
|
||||
while self
|
||||
.next()
|
||||
.is_some_and(|n| n.token != Token::Symbol(symbol))
|
||||
{}
|
||||
}
|
||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
||||
while self
|
||||
.peek()
|
||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
||||
{
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
||||
loop {
|
||||
if f(self.peek()?) {
|
||||
return self.peek();
|
||||
}
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), ParserError> {
|
||||
self.expect_token(Token::Keyword(kw))
|
||||
}
|
||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
||||
self.next.as_ref()
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
||||
&mut self.cursor
|
||||
}
|
||||
pub fn prev_end(&self) -> FilePos {
|
||||
self.prev_end
|
||||
}
|
||||
pub fn next_pos(&self) -> FilePos {
|
||||
self.next_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for TokenCursor<'a> {
|
||||
fn from(string: &'a str) -> Self {
|
||||
Self::from(CharCursor::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
||||
let cur = TokenInstance::parse(&mut cursor);
|
||||
Self {
|
||||
cursor,
|
||||
next: cur,
|
||||
next_pos: FilePos::start(),
|
||||
prev_end: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
use super::{
|
||||
token::{FileSpan, TokenInstance},
|
||||
FilePos,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParserError {
|
||||
pub msg: String,
|
||||
pub spans: Vec<FileSpan>,
|
||||
}
|
||||
|
||||
pub struct ParserErrors {
|
||||
pub errs: Vec<ParserError>,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
||||
ParserError {
|
||||
msg,
|
||||
spans: instances.iter().map(|i| i.span).collect(),
|
||||
}
|
||||
}
|
||||
pub fn from_msg(msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: vec![FileSpan::at(pos)],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("unexpected end of input".to_string())
|
||||
}
|
||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
||||
let t = &inst.token;
|
||||
ParserError::from_instances(
|
||||
&[inst],
|
||||
format!("unexpected token {t:?}; expected {expected}"),
|
||||
)
|
||||
}
|
||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
||||
let after = if self.spans.is_empty() { "" } else { ":" };
|
||||
writeln!(writer, "error: {}{}", self.msg, after)?;
|
||||
for span in &self.spans {
|
||||
span.write_for(writer, file)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserErrors {
|
||||
pub fn new() -> Self {
|
||||
Self { errs: Vec::new() }
|
||||
}
|
||||
pub fn add(&mut self, err: ParserError) {
|
||||
self.errs.push(err);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
use std::io::{stdout, BufRead, BufReader};
|
||||
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod node;
|
||||
mod nodes;
|
||||
mod parse;
|
||||
mod token;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
pub use parse::*;
|
||||
use token::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
let mut errors = ParserErrors::new();
|
||||
let res = Module::parse_node(&mut TokenCursor::from(file), &mut errors);
|
||||
println!("{:?}", res.node);
|
||||
if errors.errs.is_empty() {
|
||||
let module = res.node.resolve().expect("what");
|
||||
}
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(out, file).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_stdin() {
|
||||
for line in BufReader::new(std::io::stdin()).lines() {
|
||||
let mut errors = ParserErrors::new();
|
||||
let str = &line.expect("failed to read line");
|
||||
let mut cursor = TokenCursor::from(&str[..]);
|
||||
if let Ok(expr) = Statement::parse_node(&mut cursor, &mut errors).node.as_ref() {
|
||||
if cursor.next().is_none() {
|
||||
println!("{:?}", expr);
|
||||
} else {
|
||||
println!("uhhhh ehehe");
|
||||
}
|
||||
}
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(out, str).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::FileSpan;
|
||||
|
||||
pub trait MaybeResolved {
|
||||
type Inner<T>;
|
||||
}
|
||||
|
||||
pub struct Resolved;
|
||||
impl MaybeResolved for Resolved {
|
||||
type Inner<T> = T;
|
||||
}
|
||||
|
||||
pub struct Unresolved;
|
||||
impl MaybeResolved for Unresolved {
|
||||
type Inner<T> = Result<T, ()>;
|
||||
}
|
||||
|
||||
pub struct Node<T, R: MaybeResolved> {
|
||||
pub inner: <R as MaybeResolved>::Inner<T>,
|
||||
pub span: FileSpan,
|
||||
}
|
||||
|
||||
impl<T> Node<T, Unresolved> {
|
||||
pub fn new(inner: T, span: FileSpan) -> Self {
|
||||
Self {
|
||||
inner: Ok(inner),
|
||||
span,
|
||||
}
|
||||
}
|
||||
pub fn bx(self) -> Node<Box<T>, Unresolved> {
|
||||
Node {
|
||||
inner: self.inner.map(|v| Box::new(v)),
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: MaybeResolved> Deref for Node<T, R> {
|
||||
type Target = <R as MaybeResolved>::Inner<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: MaybeResolved> DerefMut for Node<T, R> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T, Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.inner {
|
||||
Ok(v) => v.fmt(f),
|
||||
Err(_) => f.write_str("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T, Resolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Resolvable<Res> {
|
||||
fn resolve(self) -> Result<Res, ()>;
|
||||
}
|
||||
|
||||
impl<T: Resolvable<Res>, Res> Resolvable<Node<Res, Resolved>> for Node<T, Unresolved> {
|
||||
fn resolve(self) -> Result<Node<Res, Resolved>, ()> {
|
||||
if let Ok(inner) = self.inner {
|
||||
return Ok(Node {
|
||||
inner: inner.resolve()?,
|
||||
span: self.span,
|
||||
});
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Resolvable<Res>, Res> Resolvable<Box<Res>> for Box<T> {
|
||||
fn resolve(self) -> Result<Box<Res>, ()> {
|
||||
Ok(Box::new((*self).resolve()?))
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
token::Symbol, MaybeResolved, Node, NodeParsable, Parsable, ParseResult, ParserError,
|
||||
ParserErrors, Resolvable, Resolved, Statement, TokenCursor, Unresolved,
|
||||
};
|
||||
use crate::util::Padder;
|
||||
|
||||
pub struct Body<R: MaybeResolved> {
|
||||
pub statements: Vec<Node<Statement<R>, R>>,
|
||||
}
|
||||
|
||||
impl Parsable for Body<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let mut statements = Vec::new();
|
||||
cursor.expect_sym(Symbol::OpenCurly)?;
|
||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseCurly) {
|
||||
cursor.next();
|
||||
return ParseResult::Ok(Self { statements });
|
||||
}
|
||||
let mut expect_semi = false;
|
||||
let mut recover = false;
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
recover = true;
|
||||
errors.add(ParserError::unexpected_end());
|
||||
break;
|
||||
};
|
||||
if next.is_symbol(Symbol::CloseCurly) {
|
||||
cursor.next();
|
||||
break;
|
||||
}
|
||||
if next.is_symbol(Symbol::Semicolon) {
|
||||
cursor.next();
|
||||
expect_semi = false;
|
||||
continue;
|
||||
} else if expect_semi {
|
||||
errors.add(ParserError {
|
||||
msg: "expected ';'".to_string(),
|
||||
spans: vec![cursor.next_pos().char_span()],
|
||||
});
|
||||
}
|
||||
let res = Statement::parse_node(cursor, errors);
|
||||
statements.push(res.node);
|
||||
expect_semi = true;
|
||||
if res.recover {
|
||||
cursor.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
|
||||
if cursor.peek().is_none() {
|
||||
recover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseResult::from_recover(Self { statements }, recover)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Body<Resolved>> for Body<Unresolved> {
|
||||
fn resolve(self) -> Result<Body<Resolved>, ()> {
|
||||
Ok(Body {
|
||||
statements: self
|
||||
.statements
|
||||
.into_iter()
|
||||
.map(|s| s.resolve())
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Body<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.statements.first().is_some() {
|
||||
f.write_str("{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
for s in &self.statements {
|
||||
// they don't expose wrap_buf :grief:
|
||||
padder.write_str(&format!("{s:?}\n"))?;
|
||||
}
|
||||
f.write_char('}')?;
|
||||
} else {
|
||||
f.write_str("{}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
BinaryOperator, Body, Ident, Literal, MaybeResolved, Node, NodeParsable, Parsable, ParseResult,
|
||||
ParserError, ParserErrors, Resolvable, Resolved, Symbol, TokenCursor, UnaryOperator,
|
||||
Unresolved,
|
||||
};
|
||||
|
||||
type BoxNode<R> = Node<Box<Expr<R>>, R>;
|
||||
|
||||
pub enum Expr<R: MaybeResolved> {
|
||||
Lit(Node<Literal, R>),
|
||||
Ident(Node<Ident, R>),
|
||||
BinaryOp(BinaryOperator, BoxNode<R>, BoxNode<R>),
|
||||
UnaryOp(UnaryOperator, BoxNode<R>),
|
||||
Block(Node<Body<R>, R>),
|
||||
Call(BoxNode<R>, Vec<Node<Expr<R>, R>>),
|
||||
Group(BoxNode<R>),
|
||||
}
|
||||
|
||||
impl Parsable for Expr<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let start = cursor.next_pos();
|
||||
let next = cursor.expect_peek()?;
|
||||
let mut e1 = if next.is_symbol(Symbol::OpenParen) {
|
||||
cursor.next();
|
||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||
cursor.next();
|
||||
return ParseResult::Ok(Expr::Lit(Node::new(
|
||||
Literal::Unit,
|
||||
cursor.next_pos().char_span(),
|
||||
)));
|
||||
}
|
||||
let res = Node::parse(cursor, errors);
|
||||
if res.recover {
|
||||
cursor.seek_sym(Symbol::CloseParen);
|
||||
}
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
Self::Group(res.node.bx())
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
Self::Block(Body::parse_node(cursor, errors)?)
|
||||
} else if let Some(op) = UnaryOperator::from_token(next) {
|
||||
cursor.next();
|
||||
return Node::parse(cursor, errors).map(|n| {
|
||||
let n = n.bx();
|
||||
if let Ok(box Self::BinaryOp(op2, n1, n2)) = n.inner {
|
||||
let span = start.to(n1.span.end);
|
||||
Self::BinaryOp(op2, Node::new(Self::UnaryOp(op, n1), span).bx(), n2)
|
||||
} else {
|
||||
Self::UnaryOp(op, n)
|
||||
}
|
||||
});
|
||||
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
||||
Self::Lit(val)
|
||||
} else {
|
||||
let res = Node::parse(cursor, &mut ParserErrors::new());
|
||||
if res.node.is_ok() {
|
||||
Self::Ident(res.node)
|
||||
} else {
|
||||
let next = cursor.expect_peek()?;
|
||||
return ParseResult::Err(ParserError::unexpected_token(next, "an expression"));
|
||||
}
|
||||
};
|
||||
let Some(mut next) = cursor.peek() else {
|
||||
return ParseResult::Ok(e1);
|
||||
};
|
||||
while next.is_symbol(Symbol::OpenParen) {
|
||||
cursor.next();
|
||||
let mut args = Vec::new();
|
||||
loop {
|
||||
let next = cursor.expect_peek()?;
|
||||
if next.is_symbol(Symbol::CloseParen) {
|
||||
break;
|
||||
}
|
||||
let res = Node::<Expr<Unresolved>, Unresolved>::parse(cursor, errors);
|
||||
args.push(res.node);
|
||||
if res.recover {
|
||||
cursor.seek_syms(&[Symbol::CloseParen, Symbol::Comma]);
|
||||
}
|
||||
let next = cursor.expect_peek()?;
|
||||
if !next.is_symbol(Symbol::Comma) {
|
||||
break;
|
||||
}
|
||||
cursor.next();
|
||||
}
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
let end = cursor.prev_end();
|
||||
e1 = Self::Call(Node::new(Box::new(e1), start.to(end)), args);
|
||||
let Some(next2) = cursor.peek() else {
|
||||
return ParseResult::Ok(e1);
|
||||
};
|
||||
next = next2
|
||||
}
|
||||
let end = cursor.prev_end();
|
||||
let mut recover = false;
|
||||
let res = if let Some(mut op) = BinaryOperator::from_token(&next.token) {
|
||||
cursor.next();
|
||||
let mut n1 = Node::new(e1, start.to(end)).bx();
|
||||
let res = Node::parse(cursor, errors);
|
||||
let mut n2 = res.node.bx();
|
||||
recover = res.recover;
|
||||
if let Ok(box Self::BinaryOp(op2, _, _)) = n2.as_ref() {
|
||||
if op.presedence() > op2.presedence() {
|
||||
let Ok(box Self::BinaryOp(op2, n21, n22)) = n2.inner else {
|
||||
unreachable!();
|
||||
};
|
||||
let end = n21.span.end;
|
||||
n1 = Node::new(Self::BinaryOp(op, n1, n21), start.to(end)).bx();
|
||||
op = op2;
|
||||
n2 = n22;
|
||||
}
|
||||
}
|
||||
Self::BinaryOp(op, n1, n2)
|
||||
} else {
|
||||
e1
|
||||
};
|
||||
ParseResult::from_recover(res, recover)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Expr<Resolved>> for Expr<Unresolved> {
|
||||
fn resolve(self) -> Result<Expr<Resolved>, ()> {
|
||||
Ok(match self {
|
||||
Expr::Lit(l) => Expr::Lit(l.resolve()?),
|
||||
Expr::Ident(n) => Expr::Ident(n.resolve()?),
|
||||
Expr::BinaryOp(o, e1, e2) => Expr::BinaryOp(o, e1.resolve()?, e2.resolve()?),
|
||||
Expr::UnaryOp(o, e) => Expr::UnaryOp(o, e.resolve()?),
|
||||
Expr::Block(b) => Expr::Block(b.resolve()?),
|
||||
Expr::Call(f, args) => Expr::Call(
|
||||
f.resolve()?,
|
||||
args.into_iter()
|
||||
.map(|arg| arg.resolve())
|
||||
.collect::<Result<_, ()>>()?,
|
||||
),
|
||||
Expr::Group(e) => Expr::Group(e.resolve()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expr::Lit(c) => c.fmt(f)?,
|
||||
Expr::Ident(n) => n.fmt(f)?,
|
||||
Expr::Block(b) => b.fmt(f)?,
|
||||
Expr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
Expr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
Expr::UnaryOp(op, e) => {
|
||||
write!(f, "(")?;
|
||||
write!(f, "{}", op.str())?;
|
||||
write!(f, "{:?})", *e)?;
|
||||
}
|
||||
Expr::Group(inner) => inner.fmt(f)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
use super::{
|
||||
Body, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable,
|
||||
Resolved, Symbol, TokenCursor, Unresolved,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct Function<R: MaybeResolved> {
|
||||
pub name: Node<Ident, R>,
|
||||
pub body: Node<Body<R>, R>,
|
||||
}
|
||||
|
||||
impl Parsable for Function<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
cursor.expect_kw(Keyword::Fn)?;
|
||||
let name = Node::parse(cursor, errors)?;
|
||||
cursor.expect_sym(Symbol::OpenParen)?;
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
Node::parse(cursor, errors).map(|body| Self { name, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Function<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
self.name.fmt(f)?;
|
||||
f.write_str("() ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Function<Resolved>> for Function<Unresolved> {
|
||||
fn resolve(self) -> Result<Function<Resolved>, ()> {
|
||||
Ok(Function {
|
||||
name: self.name.resolve()?,
|
||||
body: self.body.resolve()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use super::{Parsable, ParseResult, ParserError, Resolvable, Token};
|
||||
|
||||
pub struct Ident(String);
|
||||
|
||||
impl Ident {
|
||||
pub fn val(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for Ident {
|
||||
fn parse(cursor: &mut super::TokenCursor, errors: &mut super::ParserErrors) -> ParseResult<Self> {
|
||||
let next = cursor.expect_peek()?;
|
||||
let Token::Ident(name) = &next.token else {
|
||||
return ParseResult::Err(ParserError::unexpected_token(next, "an identifier"));
|
||||
};
|
||||
let name = name.to_string();
|
||||
cursor.next();
|
||||
ParseResult::Ok(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Ident> for Ident {
|
||||
fn resolve(self) -> Result<Ident, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use super::{
|
||||
CharCursor, MaybeParsable, ParserError, ParserErrors, Resolvable, Symbol, Token, TokenCursor,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
Char(char),
|
||||
Number(Number),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Number {
|
||||
pub whole: String,
|
||||
pub decimal: Option<String>,
|
||||
pub ty: Option<String>,
|
||||
}
|
||||
|
||||
impl MaybeParsable for Literal {
|
||||
fn maybe_parse(
|
||||
cursor: &mut TokenCursor,
|
||||
_: &mut ParserErrors,
|
||||
) -> Result<Option<Self>, ParserError> {
|
||||
let inst = cursor.expect_peek()?;
|
||||
let mut res = match &inst.token {
|
||||
Token::Symbol(Symbol::SingleQuote) => {
|
||||
let chars = cursor.chars();
|
||||
let c = chars.expect_next()?;
|
||||
chars.expect('\'')?;
|
||||
Self::Char(c)
|
||||
}
|
||||
Token::Symbol(Symbol::DoubleQuote) => Self::String(string_from(cursor.chars())?),
|
||||
Token::Ident(text) => {
|
||||
let first = text.chars().next().unwrap();
|
||||
if first.is_ascii_digit() {
|
||||
Self::Number(Number {
|
||||
whole: text.to_string(),
|
||||
decimal: None,
|
||||
ty: None,
|
||||
})
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
_ => return Ok(None),
|
||||
};
|
||||
cursor.next();
|
||||
if let (Some(next), Self::Number(num)) = (cursor.peek(), &mut res) {
|
||||
if next.token.is_symbol(Symbol::Dot) {
|
||||
cursor.next();
|
||||
if let Some(next) = cursor.peek() {
|
||||
if let Token::Ident(i) = &next.token {
|
||||
if i.chars().next().unwrap().is_ascii_digit() {
|
||||
num.decimal = Some(i.to_string());
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(res))
|
||||
}
|
||||
}
|
||||
pub fn string_from(cursor: &mut CharCursor) -> Result<String, ParserError> {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let c = cursor.expect_next()?;
|
||||
if c == '"' {
|
||||
return Ok(str);
|
||||
}
|
||||
str.push(match c {
|
||||
'\\' => {
|
||||
let next = cursor.expect_next()?;
|
||||
match next {
|
||||
'"' => '"',
|
||||
'\'' => '\'',
|
||||
't' => '\t',
|
||||
'n' => '\n',
|
||||
'0' => '\0',
|
||||
_ => {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => c,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Literal> for Literal {
|
||||
fn resolve(self) -> Result<Literal, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(str) => str.fmt(f),
|
||||
Self::Char(c) => c.fmt(f),
|
||||
Self::Number(n) => n.fmt(f),
|
||||
Self::Unit => f.write_str("()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Number {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.whole)?;
|
||||
if let Some(d) = &self.decimal {
|
||||
write!(f, ".{}", d)?;
|
||||
}
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, "T{}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
mod body;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod module;
|
||||
mod op;
|
||||
mod statement;
|
||||
mod lit;
|
||||
mod ident;
|
||||
|
||||
pub use body::*;
|
||||
pub use expr::*;
|
||||
pub use func::*;
|
||||
pub use module::*;
|
||||
pub use op::*;
|
||||
pub use statement::*;
|
||||
pub use lit::*;
|
||||
pub use ident::*;
|
||||
|
||||
use super::*;
|
||||
@@ -1,48 +0,0 @@
|
||||
use super::{
|
||||
Function, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserError, ParserErrors,
|
||||
Resolvable, Resolved, TokenCursor, Unresolved,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct Module<R: MaybeResolved> {
|
||||
pub functions: Vec<Node<Function<R>, R>>,
|
||||
}
|
||||
|
||||
impl Parsable for Module<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let mut functions = Vec::new();
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return ParseResult::Ok(Self { functions });
|
||||
};
|
||||
if next.is_keyword(Keyword::Fn) {
|
||||
let res = Node::parse(cursor, errors);
|
||||
functions.push(res.node);
|
||||
if res.recover {
|
||||
return ParseResult::Recover(Self { functions });
|
||||
}
|
||||
} else {
|
||||
errors.add(ParserError::unexpected_token(next, "fn"));
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Module<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.functions.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Module<Resolved>> for Module<Unresolved> {
|
||||
fn resolve(self) -> Result<Module<Resolved>, ()> {
|
||||
Ok(Module {
|
||||
functions: self
|
||||
.functions
|
||||
.into_iter()
|
||||
.map(|f| f.resolve())
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use super::{Symbol, Token};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BinaryOperator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Access,
|
||||
Assign,
|
||||
}
|
||||
|
||||
impl BinaryOperator {
|
||||
pub fn presedence(&self) -> u32 {
|
||||
match self {
|
||||
Self::Assign => 0,
|
||||
Self::LessThan => 1,
|
||||
Self::GreaterThan => 1,
|
||||
Self::Add => 2,
|
||||
Self::Sub => 3,
|
||||
Self::Mul => 4,
|
||||
Self::Div => 5,
|
||||
Self::Access => 6,
|
||||
}
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::Access => ".",
|
||||
Self::Assign => "=",
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::OpenAngle => Self::LessThan,
|
||||
Symbol::CloseAngle => Self::GreaterThan,
|
||||
Symbol::Plus => Self::Add,
|
||||
Symbol::Minus => Self::Sub,
|
||||
Symbol::Asterisk => Self::Mul,
|
||||
Symbol::Slash => Self::Div,
|
||||
Symbol::Dot => Self::Access,
|
||||
Symbol::Equals => Self::Assign,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn pad(&self) -> bool {
|
||||
match self {
|
||||
Self::Add => true,
|
||||
Self::Sub => true,
|
||||
Self::Mul => true,
|
||||
Self::Div => true,
|
||||
Self::LessThan => true,
|
||||
Self::GreaterThan => true,
|
||||
Self::Access => false,
|
||||
Self::Assign => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum UnaryOperator {
|
||||
Not,
|
||||
Ref,
|
||||
}
|
||||
|
||||
impl UnaryOperator {
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Not => "!",
|
||||
Self::Ref => "&",
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::Ampersand => Self::Ref,
|
||||
Symbol::Bang => Self::Not,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
use super::{
|
||||
Expr, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable, Resolved, Symbol, Token, TokenCursor, Unresolved
|
||||
};
|
||||
|
||||
pub enum Statement<R: MaybeResolved> {
|
||||
Let(Node<Ident, R>, Node<Expr<R>, R>),
|
||||
Return(Node<Expr<R>, R>),
|
||||
Expr(Node<Expr<R>, R>),
|
||||
}
|
||||
|
||||
impl Parsable for Statement<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let next = cursor.expect_peek()?;
|
||||
match next.token {
|
||||
Token::Keyword(Keyword::Let) => {
|
||||
cursor.next();
|
||||
let name = Node::parse(cursor, errors)?;
|
||||
cursor.expect_sym(Symbol::Equals)?;
|
||||
Node::parse(cursor, errors).map(|expr| Self::Let(name, expr))
|
||||
}
|
||||
Token::Keyword(Keyword::Return) => {
|
||||
cursor.next();
|
||||
Node::parse(cursor, errors).map(Self::Return)
|
||||
}
|
||||
_ => Node::parse(cursor, errors).map(Self::Expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Statement<Resolved>> for Statement<Unresolved> {
|
||||
fn resolve(self) -> Result<Statement<Resolved>, ()> {
|
||||
Ok(match self {
|
||||
Self::Let(i, e) => Statement::Let(i.resolve()?, e.resolve()?),
|
||||
Self::Return(e) => Statement::Return(e.resolve()?),
|
||||
Self::Expr(e) => Statement::Expr(e.resolve()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Statement<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Statement::Let(n, e) => {
|
||||
f.write_str("let ")?;
|
||||
n.fmt(f);
|
||||
f.write_str(" = ")?;
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
Statement::Return(e) => {
|
||||
f.write_str("return ")?;
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
};
|
||||
|
||||
use super::{Node, ParserError, ParserErrors, TokenCursor, Unresolved};
|
||||
|
||||
pub enum ParseResult<T> {
|
||||
Ok(T),
|
||||
Recover(T),
|
||||
Err(ParserError),
|
||||
SubErr,
|
||||
}
|
||||
|
||||
impl<T> ParseResult<T> {
|
||||
pub fn from_recover(data: T, recover: bool) -> Self {
|
||||
if recover {
|
||||
Self::Recover(data)
|
||||
} else {
|
||||
Self::Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for ParseResult<T> {
|
||||
type Output = Result<T, T>;
|
||||
type Residual = Option<ParserError>;
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
match output {
|
||||
Ok(v) => Self::Ok(v),
|
||||
Err(v) => Self::Recover(v),
|
||||
}
|
||||
}
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
ParseResult::Ok(v) => ControlFlow::Continue(Ok(v)),
|
||||
ParseResult::Recover(v) => ControlFlow::Continue(Err(v)),
|
||||
ParseResult::Err(e) => ControlFlow::Break(Some(e)),
|
||||
ParseResult::SubErr => ControlFlow::Break(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for ParseResult<T> {
|
||||
fn from_residual(residual: <Self as Try>::Residual) -> Self {
|
||||
match residual {
|
||||
Some(err) => Self::Err(err),
|
||||
None => Self::SubErr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual<Result<Infallible, ParserError>> for ParseResult<T> {
|
||||
fn from_residual(residual: Result<Infallible, ParserError>) -> Self {
|
||||
match residual {
|
||||
Err(e) => Self::Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> FromResidual<ParseResult<T>> for ParseResult<U> {
|
||||
fn from_residual(residual: ParseResult<T>) -> Self {
|
||||
match residual {
|
||||
ParseResult::Err(e) => Self::Err(e),
|
||||
ParseResult::SubErr => Self::SubErr,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeParseResult<T> {
|
||||
pub node: Node<T, Unresolved>,
|
||||
pub recover: bool,
|
||||
}
|
||||
|
||||
impl<T> NodeParseResult<T> {
|
||||
pub fn map<F: FnOnce(Node<T, Unresolved>) -> U, U>(self, op: F) -> ParseResult<U> {
|
||||
let res = op(self.node);
|
||||
if self.recover {
|
||||
ParseResult::Recover(res)
|
||||
} else {
|
||||
ParseResult::Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for NodeParseResult<T> {
|
||||
type Output = Node<T, Unresolved>;
|
||||
type Residual = ParseResult<T>;
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self {
|
||||
node: output,
|
||||
recover: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
if self.recover {
|
||||
ControlFlow::Break(ParseResult::SubErr)
|
||||
} else {
|
||||
ControlFlow::Continue(self.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for NodeParseResult<T> {
|
||||
fn from_residual(_: <Self as Try>::Residual) -> Self {
|
||||
// I hope this is unreachable ???
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Parsable: Sized {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self>;
|
||||
}
|
||||
|
||||
pub trait MaybeParsable: Sized {
|
||||
fn maybe_parse(
|
||||
cursor: &mut TokenCursor,
|
||||
errors: &mut ParserErrors,
|
||||
) -> Result<Option<Self>, ParserError>;
|
||||
}
|
||||
|
||||
impl<T: Parsable> Node<T, Unresolved> {
|
||||
pub fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<T> {
|
||||
let start = cursor.next_pos();
|
||||
let (inner, recover) = match T::parse(cursor, errors) {
|
||||
ParseResult::Ok(v) => (Ok(v), false),
|
||||
ParseResult::Recover(v) => (Ok(v), true),
|
||||
ParseResult::Err(e) => {
|
||||
errors.add(e);
|
||||
(Err(()), true)
|
||||
}
|
||||
ParseResult::SubErr => (Err(()), true),
|
||||
};
|
||||
let end = cursor.prev_end();
|
||||
NodeParseResult {
|
||||
node: Self {
|
||||
inner,
|
||||
span: start.to(end),
|
||||
},
|
||||
recover,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MaybeParsable> Node<T, Unresolved> {
|
||||
pub fn maybe_parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> Option<Self> {
|
||||
let start = cursor.next_pos();
|
||||
let inner = match T::maybe_parse(cursor, errors) {
|
||||
Ok(v) => Ok(v?),
|
||||
Err(e) => {
|
||||
errors.add(e);
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
let end = cursor.prev_end();
|
||||
Some(Self {
|
||||
inner,
|
||||
span: start.to(end),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeParsable {
|
||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
impl<T: Parsable> NodeParsable for T {
|
||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Node::<Self, Unresolved>::parse(cursor, errors)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
use super::super::ParserError;
|
||||
use super::FilePos;
|
||||
|
||||
pub struct CharCursor<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
next_pos: FilePos,
|
||||
prev_pos: FilePos,
|
||||
}
|
||||
|
||||
impl CharCursor<'_> {
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let res = self.peek()?;
|
||||
self.advance();
|
||||
Some(res)
|
||||
}
|
||||
pub fn expect(&mut self, c: char) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == c {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::at(
|
||||
self.prev_pos,
|
||||
format!("unexpected char '{next}'; expected '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
pub fn advance(&mut self) {
|
||||
let Some(next) = self.chars.next() else {
|
||||
return;
|
||||
};
|
||||
self.prev_pos = self.next_pos;
|
||||
if next == '\n' {
|
||||
self.next_pos.col = 0;
|
||||
self.next_pos.line += 1;
|
||||
} else {
|
||||
self.next_pos.col += 1;
|
||||
}
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
||||
self.next().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn next_pos(&self) -> FilePos {
|
||||
self.next_pos
|
||||
}
|
||||
pub fn prev_pos(&self) -> FilePos {
|
||||
self.prev_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self {
|
||||
chars: value.chars().peekable(),
|
||||
next_pos: FilePos::start(),
|
||||
prev_pos: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FilePos {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileSpan {
|
||||
pub start: FilePos,
|
||||
pub end: FilePos,
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn start() -> Self {
|
||||
Self { line: 0, col: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn to(self, end: FilePos) -> FileSpan {
|
||||
FileSpan { start: self, end }
|
||||
}
|
||||
pub fn char_span(self) -> FileSpan {
|
||||
FileSpan::at(self)
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE: usize = 1;
|
||||
const AFTER: usize = 0;
|
||||
|
||||
impl FileSpan {
|
||||
pub fn at(pos: FilePos) -> Self {
|
||||
Self {
|
||||
start: pos,
|
||||
end: pos,
|
||||
}
|
||||
}
|
||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
||||
let start = self.start.line.saturating_sub(BEFORE);
|
||||
let num_before = self.start.line - start;
|
||||
let mut lines = file.lines().skip(start);
|
||||
let width = format!("{}", self.end.line + AFTER).len();
|
||||
let same_line = self.start.line == self.end.line;
|
||||
for i in 0..num_before {
|
||||
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
|
||||
let len = if same_line {
|
||||
self.end.col - self.start.col + 1
|
||||
} else {
|
||||
line.len() - self.start.col
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
" ".repeat(self.start.col) + &"^".repeat(len)
|
||||
)?;
|
||||
if !same_line {
|
||||
for _ in 0..self.end.line - self.start.line - 1 {
|
||||
lines.next();
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
"^".repeat(self.end.col + 1)
|
||||
)?;
|
||||
}
|
||||
// for i in 0..AFTER {
|
||||
// if let Some(next) = lines.next() {
|
||||
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
|
||||
// }
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Keyword {
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Return,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
pub fn from_string(str: &str) -> Option<Self> {
|
||||
Some(match str {
|
||||
"fn" => Self::Fn,
|
||||
"let" => Self::Let,
|
||||
"if" => Self::If,
|
||||
"return" => Self::Return,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
mod cursor;
|
||||
mod file;
|
||||
mod keyword;
|
||||
mod symbol;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use file::*;
|
||||
pub use keyword::*;
|
||||
pub use symbol::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Token {
|
||||
Symbol(Symbol),
|
||||
Ident(String),
|
||||
Keyword(Keyword),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenInstance {
|
||||
pub token: Token,
|
||||
pub span: FileSpan,
|
||||
}
|
||||
|
||||
impl TokenInstance {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<TokenInstance> {
|
||||
cursor.skip_whitespace();
|
||||
cursor.peek()?;
|
||||
let start = cursor.next_pos();
|
||||
if let Some(s) = Symbol::parse(cursor) {
|
||||
if s == Symbol::DoubleSlash {
|
||||
while cursor.next() != Some('\n') {}
|
||||
return Self::parse(cursor);
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
return Some(Self {
|
||||
token: Token::Symbol(s),
|
||||
span: FileSpan { start, end },
|
||||
});
|
||||
}
|
||||
let mut word = String::new();
|
||||
while let Some(c) = cursor.peek() {
|
||||
if c.is_whitespace() || Symbol::from_char(c).is_some() {
|
||||
break;
|
||||
}
|
||||
word.push(c);
|
||||
cursor.advance();
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
let token = if let Some(keyword) = Keyword::from_string(&word) {
|
||||
Token::Keyword(keyword)
|
||||
} else {
|
||||
Token::Ident(word)
|
||||
};
|
||||
Some(Self {
|
||||
token,
|
||||
span: FileSpan { start, end },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn is_symbol(&self, symbol: Symbol) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => *s == symbol,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_symbol_and(&self, f: impl Fn(Symbol) -> bool) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => f(*s),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyword(&self, kw: Keyword) -> bool {
|
||||
match self {
|
||||
Token::Keyword(k) => *k == kw,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TokenInstance {
|
||||
type Target = Token;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::CharCursor;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Symbol {
|
||||
Semicolon,
|
||||
Colon,
|
||||
DoubleColon,
|
||||
Equals,
|
||||
DoubleEquals,
|
||||
Arrow,
|
||||
DoubleArrow,
|
||||
Plus,
|
||||
Minus,
|
||||
Asterisk,
|
||||
Slash,
|
||||
DoubleSlash,
|
||||
Dot,
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
OpenCurly,
|
||||
CloseCurly,
|
||||
OpenSquare,
|
||||
CloseSquare,
|
||||
OpenAngle,
|
||||
CloseAngle,
|
||||
SingleQuote,
|
||||
DoubleQuote,
|
||||
Bang,
|
||||
Ampersand,
|
||||
DoubleAmpersand,
|
||||
Pipe,
|
||||
DoublePipe,
|
||||
Comma,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
||||
Self::from_char(cursor.peek()?).map(|mut s| {
|
||||
cursor.advance();
|
||||
s.finish(cursor);
|
||||
s
|
||||
})
|
||||
}
|
||||
pub fn from_char(c: char) -> Option<Self> {
|
||||
Some(match c {
|
||||
'(' => Self::OpenParen,
|
||||
')' => Self::CloseParen,
|
||||
'[' => Self::OpenSquare,
|
||||
']' => Self::CloseSquare,
|
||||
'{' => Self::OpenCurly,
|
||||
'}' => Self::CloseCurly,
|
||||
'<' => Self::OpenAngle,
|
||||
'>' => Self::CloseAngle,
|
||||
';' => Self::Semicolon,
|
||||
':' => Self::Colon,
|
||||
'+' => Self::Plus,
|
||||
'-' => Self::Minus,
|
||||
'*' => Self::Asterisk,
|
||||
'/' => Self::Slash,
|
||||
'=' => Self::Equals,
|
||||
'.' => Self::Dot,
|
||||
'\'' => Self::SingleQuote,
|
||||
'"' => Self::DoubleQuote,
|
||||
'!' => Self::Bang,
|
||||
'&' => Self::Ampersand,
|
||||
'|' => Self::Pipe,
|
||||
',' => Self::Comma,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
pub fn finish(&mut self, cursor: &mut CharCursor) {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return;
|
||||
};
|
||||
*self = match self {
|
||||
Self::Colon => match next {
|
||||
':' => Self::DoubleColon,
|
||||
_ => return,
|
||||
},
|
||||
Self::Minus => match next {
|
||||
'>' => Self::Arrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Equals => match next {
|
||||
'=' => Self::DoubleEquals,
|
||||
'>' => Self::DoubleArrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Slash => match next {
|
||||
'/' => Self::DoubleSlash,
|
||||
_ => return,
|
||||
},
|
||||
Self::Ampersand => match next {
|
||||
'&' => Self::DoubleAmpersand,
|
||||
_ => return,
|
||||
},
|
||||
Self::Pipe => match next {
|
||||
'&' => Self::DoublePipe,
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
cursor.advance();
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Semicolon => ";",
|
||||
Self::Colon => ":",
|
||||
Self::DoubleColon => "::",
|
||||
Self::Equals => "=",
|
||||
Self::DoubleEquals => "==",
|
||||
Self::Arrow => "->",
|
||||
Self::DoubleArrow => "=>",
|
||||
Self::Plus => "+",
|
||||
Self::Minus => "-",
|
||||
Self::Asterisk => "*",
|
||||
Self::Slash => "/",
|
||||
Self::DoubleSlash => "//",
|
||||
Self::Dot => ".",
|
||||
Self::OpenParen => "(",
|
||||
Self::CloseParen => ")",
|
||||
Self::OpenCurly => "{",
|
||||
Self::CloseCurly => "}",
|
||||
Self::OpenSquare => "[",
|
||||
Self::CloseSquare => "]",
|
||||
Self::OpenAngle => "<",
|
||||
Self::CloseAngle => ">",
|
||||
Self::SingleQuote => "'",
|
||||
Self::DoubleQuote => "\"",
|
||||
Self::Bang => "!",
|
||||
Self::Comma => ",",
|
||||
Self::Ampersand => "&",
|
||||
Self::DoubleAmpersand => "&&",
|
||||
Self::Pipe => "|",
|
||||
Self::DoublePipe => "||",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Symbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{}'", self.str())
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Debug, Write};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::util::Padder;
|
||||
|
||||
use super::util::WHITESPACE_SET;
|
||||
use super::CharCursor;
|
||||
use super::Expr;
|
||||
use super::ParserError;
|
||||
|
||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
set.extend(&['(']);
|
||||
set
|
||||
});
|
||||
|
||||
pub struct Body {
|
||||
statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Let(String, Expr),
|
||||
Return(Expr),
|
||||
Expr(Expr),
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let mut statements = Vec::new();
|
||||
cursor.expect_char('{')?;
|
||||
loop {
|
||||
cursor.skip_whitespace();
|
||||
let next = cursor.expect_peek()?;
|
||||
if next == '}' {
|
||||
cursor.next();
|
||||
return Ok(Self { statements });
|
||||
}
|
||||
statements.push(Statement::parse(cursor)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
Ok(if cursor.advance_if_str("let", &WHITESPACE_SET) {
|
||||
cursor.skip_whitespace();
|
||||
let name = cursor.until(&NAME_END);
|
||||
if name.is_empty() {
|
||||
return Err(ParserError::at(
|
||||
cursor.pos(),
|
||||
"Expected variable name".to_string(),
|
||||
));
|
||||
}
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char('=')?;
|
||||
let expr = Expr::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(';')?;
|
||||
Self::Let(name, expr)
|
||||
} else if cursor.advance_if_str("return", &WHITESPACE_SET) {
|
||||
let expr = Expr::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(';')?;
|
||||
Self::Return(expr)
|
||||
} else {
|
||||
let expr = Expr::parse(cursor)?;
|
||||
match cursor.expect_peek()? {
|
||||
';' => {
|
||||
cursor.next();
|
||||
Self::Expr(expr)
|
||||
}
|
||||
'}' => Self::Return(expr),
|
||||
_ => {
|
||||
cursor.next();
|
||||
return Err(ParserError::at(
|
||||
cursor.prev_pos(),
|
||||
"unexpected end of statement; expected a ';' or '}'".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Statement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Statement::Let(n, e) => {
|
||||
write!(f, "let {n} = {e:?};")?;
|
||||
}
|
||||
Statement::Return(e) => {
|
||||
write!(f, "return {e:?};")?;
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
write!(f, "{e:?};")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Body {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.statements.first().is_some() {
|
||||
write!(f, "{{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
for s in &self.statements {
|
||||
// they don't expose wrap_buf :grief:
|
||||
writeln!(padder, "{s:?}")?;
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
} else {
|
||||
write!(f, "{{}}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
use std::{collections::HashSet, iter::Peekable, str::Chars};
|
||||
|
||||
use super::{error::ParserError, util::WHITESPACE_SET};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FilePos {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileRegion {
|
||||
pub start: FilePos,
|
||||
pub end: FilePos,
|
||||
}
|
||||
|
||||
pub struct CharCursor<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
pos: FilePos,
|
||||
prev_pos: FilePos,
|
||||
}
|
||||
|
||||
impl CharCursor<'_> {
|
||||
pub fn until(&mut self, set: &HashSet<char>) -> String {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let Some(next) = self.peek() else {
|
||||
return str;
|
||||
};
|
||||
if set.contains(&next) {
|
||||
return str;
|
||||
}
|
||||
str.push(next);
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
||||
self.advance();
|
||||
}
|
||||
let mut copy = self.chars.clone();
|
||||
if let Some('/') = copy.next() {
|
||||
if let Some('/') = copy.next() {
|
||||
self.advance();
|
||||
self.advance();
|
||||
while self.next() != Some('\n') {}
|
||||
self.skip_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let res = self.peek()?;
|
||||
self.advance();
|
||||
Some(res)
|
||||
}
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
pub fn advance(&mut self) {
|
||||
self.prev_pos = self.pos;
|
||||
if self.peek().is_some_and(|c| c == '\n') {
|
||||
self.pos.col = 0;
|
||||
self.pos.line += 1;
|
||||
} else {
|
||||
self.pos.col += 1;
|
||||
}
|
||||
self.chars.next();
|
||||
}
|
||||
pub fn advance_if(&mut self, c: char) -> bool {
|
||||
if let Some(c2) = self.peek() {
|
||||
if c2 == c {
|
||||
self.advance();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn advance_if_str(&mut self, exp: &str, end: &HashSet<char>) -> bool {
|
||||
let mut new = self.chars.clone();
|
||||
for e in exp.chars() {
|
||||
let Some(c) = new.next() else {
|
||||
return false;
|
||||
};
|
||||
if e != c {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if new.peek().is_some_and(|c| !end.contains(c)) {
|
||||
return false;
|
||||
}
|
||||
for _ in 0..exp.len() {
|
||||
self.advance();
|
||||
}
|
||||
true
|
||||
}
|
||||
pub fn expect_char(&mut self, c: char) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == c {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::at(
|
||||
self.prev_pos,
|
||||
format!("unexpected char '{next}'; expected '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
||||
self.next().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<char, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn pos(&self) -> FilePos {
|
||||
self.pos
|
||||
}
|
||||
pub fn prev_pos(&self) -> FilePos {
|
||||
self.prev_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self {
|
||||
chars: value.chars().peekable(),
|
||||
pos: FilePos::start(),
|
||||
prev_pos: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn start() -> Self {
|
||||
Self { line: 0, col: 0 }
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use super::{FilePos, FileRegion};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParserError {
|
||||
pub msg: String,
|
||||
pub regions: Vec<FileRegion>,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn from_msg(msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
regions: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
regions: vec![FileRegion {
|
||||
start: pos,
|
||||
end: pos,
|
||||
}],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("Unexpected end of input".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE: usize = 1;
|
||||
const AFTER: usize = 1;
|
||||
|
||||
pub fn print_error(err: ParserError, file: &str) {
|
||||
let after = if err.regions.is_empty() {""} else {":"};
|
||||
println!("error: {}{}", err.msg, after);
|
||||
for reg in err.regions {
|
||||
print_region(file, reg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_region(file: &str, reg: FileRegion) {
|
||||
let start = reg.start.line.saturating_sub(BEFORE);
|
||||
let num_before = reg.start.line - start;
|
||||
let mut lines = file.lines().skip(start);
|
||||
let len = reg.end.col - reg.start.col + 1;
|
||||
let width = format!("{}", reg.end.line + AFTER).len();
|
||||
for i in 0..num_before + 1 {
|
||||
println!("{:>width$} | {}", start + i, lines.next().unwrap());
|
||||
}
|
||||
println!(
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
" ".repeat(reg.start.col) + &"^".repeat(len)
|
||||
);
|
||||
for i in 0..AFTER {
|
||||
if let Some(next) = lines.next() {
|
||||
println!("{:>width$} | {}", reg.end.line + i + 1, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||
|
||||
static SYMBOLS: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = HashSet::new();
|
||||
for o in Operator::ALL {
|
||||
for c in o.str().chars() {
|
||||
set.insert(c);
|
||||
}
|
||||
}
|
||||
set
|
||||
});
|
||||
|
||||
static IDENT_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
let symbols = &SYMBOLS;
|
||||
set.extend(symbols.iter().chain(&[';', '(', ')']));
|
||||
set
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Val {
|
||||
String(String),
|
||||
Number(String),
|
||||
Unit,
|
||||
}
|
||||
|
||||
pub enum Expr {
|
||||
Block(Body),
|
||||
Val(Val),
|
||||
Ident(String),
|
||||
BinaryOp(Operator, Box<Expr>, Box<Expr>),
|
||||
Call(Box<Expr>, Vec<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Offset,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let Some(next) = cursor.peek() else {
|
||||
return Ok(Self::Val(Val::Unit));
|
||||
};
|
||||
let mut e1 = match next {
|
||||
'(' => {
|
||||
cursor.advance();
|
||||
let expr = Self::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(')')?;
|
||||
expr
|
||||
}
|
||||
'{' => {
|
||||
Self::Block(Body::parse(cursor)?)
|
||||
}
|
||||
_ => {
|
||||
if let Some(val) = Val::parse_nonunit(cursor)? {
|
||||
Self::Val(val)
|
||||
} else {
|
||||
let name = cursor.until(&IDENT_END);
|
||||
Self::Ident(name)
|
||||
}
|
||||
}
|
||||
};
|
||||
cursor.skip_whitespace();
|
||||
let Some(mut next) = cursor.peek() else {
|
||||
return Ok(e1);
|
||||
};
|
||||
while next == '(' {
|
||||
cursor.advance();
|
||||
let inner = Self::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(')')?;
|
||||
e1 = Self::Call(Box::new(e1), vec![inner]);
|
||||
let Some(next2) = cursor.peek() else {
|
||||
return Ok(e1);
|
||||
};
|
||||
next = next2
|
||||
}
|
||||
if let Some(op) = Operator::parse(cursor) {
|
||||
let e2 = Self::parse(cursor)?;
|
||||
return Ok(if let Self::BinaryOp(op_next, e2, e3) = e2 {
|
||||
if op.presedence() > op_next.presedence() {
|
||||
Self::BinaryOp(op_next, Box::new(Self::BinaryOp(op, Box::new(e1), e2)), e3)
|
||||
} else {
|
||||
Self::BinaryOp(op, Box::new(e1), Box::new(Self::BinaryOp(op_next, e2, e3)))
|
||||
}
|
||||
} else {
|
||||
Self::BinaryOp(op, Box::new(e1), Box::new(e2))
|
||||
});
|
||||
};
|
||||
Ok(e1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Val {
|
||||
pub fn parse_nonunit(cursor: &mut CharCursor) -> Result<Option<Self>, ParserError> {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(match next {
|
||||
'"' => {
|
||||
cursor.advance();
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let mut next = cursor.expect_next()?;
|
||||
if next == '"' {
|
||||
break;
|
||||
}
|
||||
if next == '\\' {
|
||||
next = match cursor.expect_next()? {
|
||||
'"' => '"',
|
||||
c => {
|
||||
return Err(ParserError::at(
|
||||
cursor.pos(),
|
||||
format!("unexpected escape char '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
str.push(next);
|
||||
}
|
||||
Self::String(str)
|
||||
}
|
||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
break;
|
||||
};
|
||||
match next {
|
||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
||||
str.push(next);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
cursor.advance();
|
||||
}
|
||||
Self::Number(str)
|
||||
}
|
||||
_ => {
|
||||
return Ok(None);
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
const ALL: [Self; 7] = [
|
||||
Self::Add,
|
||||
Self::Sub,
|
||||
Self::Mul,
|
||||
Self::Div,
|
||||
Self::Offset,
|
||||
Self::GreaterThan,
|
||||
Self::LessThan,
|
||||
];
|
||||
pub fn presedence(&self) -> u32 {
|
||||
match self {
|
||||
Operator::LessThan => 0,
|
||||
Operator::GreaterThan => 0,
|
||||
Operator::Add => 1,
|
||||
Operator::Sub => 2,
|
||||
Operator::Mul => 3,
|
||||
Operator::Div => 4,
|
||||
Operator::Offset => 5,
|
||||
}
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::Offset => ".",
|
||||
}
|
||||
}
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
||||
let res = match cursor.peek()? {
|
||||
'+' => Operator::Add,
|
||||
'-' => Operator::Sub,
|
||||
'*' => Operator::Mul,
|
||||
'/' => Operator::Div,
|
||||
'.' => Operator::Offset,
|
||||
_ => return None,
|
||||
};
|
||||
for _ in 0..res.str().len() {
|
||||
cursor.advance();
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
pub fn pad(&self) -> bool {
|
||||
match self {
|
||||
Operator::Add => true,
|
||||
Operator::Sub => true,
|
||||
Operator::Mul => true,
|
||||
Operator::Div => true,
|
||||
Operator::LessThan => true,
|
||||
Operator::GreaterThan => true,
|
||||
Operator::Offset => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expr::Block(b) => write!(f, "{:?}", b)?,
|
||||
Expr::Ident(n) => f.write_str(n)?,
|
||||
Expr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
Expr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
write!(f, "(")?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
write!(f, ", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
Expr::Val(v) => {
|
||||
write!(f, "{:?}", v)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod module;
|
||||
mod util;
|
||||
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
pub use module::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
match Module::parse(&mut CharCursor::from(file)) {
|
||||
Err(err) => print_error(err, file),
|
||||
Ok(module) => println!("{module:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_stdin() {
|
||||
for line in BufReader::new(std::io::stdin()).lines() {
|
||||
let str = &line.expect("failed to read line");
|
||||
let mut cursor = CharCursor::from(&str[..]);
|
||||
match Statement::parse(&mut cursor) {
|
||||
Ok(expr) => println!("{:?}", expr),
|
||||
Err(err) => print_error(err, str),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
functions: Vec<Function>,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub body: Body,
|
||||
}
|
||||
|
||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
set.extend(&['(']);
|
||||
set
|
||||
});
|
||||
|
||||
impl Module {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
let mut functions = Vec::new();
|
||||
loop {
|
||||
let next = cursor.until(&WHITESPACE_SET);
|
||||
if next.is_empty() {
|
||||
return Ok(Self { functions });
|
||||
}
|
||||
if next == "fn" {
|
||||
functions.push(Function::parse(cursor)?);
|
||||
} else {
|
||||
return Err(ParserError::at(cursor.pos(), "expected fn".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let name = cursor.until(&NAME_END);
|
||||
if name.is_empty() {
|
||||
return Err(ParserError::at(cursor.pos(), "expected function name".to_string()));
|
||||
}
|
||||
cursor.expect_char('(')?;
|
||||
cursor.expect_char(')')?;
|
||||
let body = Body::parse(cursor)?;
|
||||
Ok(Self { name, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
f.write_str(&self.name)?;
|
||||
f.write_str("() ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
pub const WHITESPACE: [char; 25] = [
|
||||
'\u{0009}', '\u{000A}', '\u{000B}', '\u{000C}', '\u{000D}', '\u{0020}', '\u{0085}', '\u{00A0}',
|
||||
'\u{1680}', '\u{2000}', '\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}',
|
||||
'\u{2007}', '\u{2008}', '\u{2009}', '\u{200A}', '\u{2028}', '\u{2029}', '\u{202F}', '\u{205F}',
|
||||
'\u{3000}',
|
||||
];
|
||||
|
||||
pub static WHITESPACE_SET: LazyLock<HashSet<char>> = LazyLock::new(|| HashSet::from_iter(WHITESPACE));
|
||||
@@ -1,53 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::common::FileID;
|
||||
|
||||
use super::{
|
||||
CompilerMsg, CompilerOutput, Node, NodeParseResult, Parsable, ParsableWith, TokenCursor,
|
||||
};
|
||||
|
||||
pub struct ParserCtx<'a> {
|
||||
pub cursor: TokenCursor<'a>,
|
||||
pub output: &'a mut CompilerOutput,
|
||||
}
|
||||
|
||||
impl<'a> Deref for ParserCtx<'a> {
|
||||
type Target = TokenCursor<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ParserCtx<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParserCtx<'a> {
|
||||
pub fn err(&mut self, msg: CompilerMsg) {
|
||||
self.output.err(msg);
|
||||
}
|
||||
pub fn hint(&mut self, msg: CompilerMsg) {
|
||||
self.output.hint(msg);
|
||||
}
|
||||
pub fn parse<T: Parsable>(&mut self) -> NodeParseResult<T> {
|
||||
Node::parse(self)
|
||||
}
|
||||
pub fn maybe_parse<T>(&mut self) -> Option<NodeParseResult<T>>
|
||||
where
|
||||
Option<T>: Parsable,
|
||||
{
|
||||
Node::maybe_parse(self)
|
||||
}
|
||||
pub fn parse_with<T: ParsableWith>(&mut self, data: T::Data) -> NodeParseResult<T> {
|
||||
Node::parse_with(self, data)
|
||||
}
|
||||
pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self {
|
||||
Self {
|
||||
cursor: TokenCursor::from_file_str(file, string),
|
||||
output,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use crate::common::FileID;
|
||||
|
||||
use super::{
|
||||
token::{CharCursor, Keyword, Symbol, Token, TokenInstance},
|
||||
CompilerMsg, FilePos,
|
||||
};
|
||||
|
||||
pub struct TokenCursor<'a> {
|
||||
cursor: CharCursor<'a>,
|
||||
next: Option<TokenInstance>,
|
||||
next_start: FilePos,
|
||||
prev_end: FilePos,
|
||||
}
|
||||
|
||||
impl<'a> TokenCursor<'a> {
|
||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
||||
self.prev_end = self.cursor.prev_pos();
|
||||
let next = TokenInstance::parse(&mut self.cursor);
|
||||
self.next_start = next
|
||||
.as_ref()
|
||||
.map(|i| i.span.end)
|
||||
.unwrap_or(FilePos::start(self.file()));
|
||||
std::mem::replace(&mut self.next, next)
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<TokenInstance, CompilerMsg> {
|
||||
self.peek().ok_or(CompilerMsg::unexpected_end())?;
|
||||
Ok(self.next().unwrap())
|
||||
}
|
||||
pub fn expect_token(&mut self, t: Token) -> Result<(), CompilerMsg> {
|
||||
let next = self.expect_next()?;
|
||||
if t == next.token {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CompilerMsg::unexpected_token(&next, &format!("{t:?}")))
|
||||
}
|
||||
}
|
||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), CompilerMsg> {
|
||||
self.expect_token(Token::Symbol(symbol))
|
||||
}
|
||||
pub fn next_on_new_line(&mut self) -> bool {
|
||||
self.next_start.line != self.prev_end.line
|
||||
}
|
||||
pub fn seek_sym(&mut self, sym: Symbol) {
|
||||
while self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
||||
}
|
||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
||||
while self
|
||||
.peek()
|
||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
||||
{
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn seek_sym_on_line(&mut self, sym: Symbol) {
|
||||
while !self.next_on_new_line() && self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
||||
}
|
||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
||||
loop {
|
||||
if f(self.peek()?) {
|
||||
return self.peek();
|
||||
}
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), CompilerMsg> {
|
||||
self.expect_token(Token::Keyword(kw))
|
||||
}
|
||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
||||
self.next.as_ref()
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, CompilerMsg> {
|
||||
self.peek().ok_or(CompilerMsg::unexpected_end())
|
||||
}
|
||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
||||
&mut self.cursor
|
||||
}
|
||||
pub fn prev_end(&self) -> FilePos {
|
||||
self.prev_end
|
||||
}
|
||||
pub fn next_start(&self) -> FilePos {
|
||||
self.next_start
|
||||
}
|
||||
pub fn from_file_str(id: FileID, string: &'a str) -> Self {
|
||||
Self::from(CharCursor::from_file_str(id, string))
|
||||
}
|
||||
pub fn file(&self) -> FileID {
|
||||
self.cursor.file()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
||||
let cur = TokenInstance::parse(&mut cursor);
|
||||
Self {
|
||||
next_start: FilePos::start(cursor.file()),
|
||||
prev_end: FilePos::start(cursor.file()),
|
||||
cursor,
|
||||
next: cur,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use super::Node;
|
||||
use super::PIdent;
|
||||
use super::CompilerMsg;
|
||||
use super::TokenInstance;
|
||||
|
||||
impl CompilerMsg {
|
||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
||||
CompilerMsg {
|
||||
msg,
|
||||
spans: instances.iter().map(|i| i.span).collect(),
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("unexpected end of input".to_string())
|
||||
}
|
||||
pub fn identifier_not_found(id: &Node<PIdent>) -> Self {
|
||||
Self {
|
||||
msg: format!("Identifier '{}' not found", id.as_ref().unwrap()),
|
||||
spans: vec![id.origin],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
||||
let t = &inst.token;
|
||||
CompilerMsg::from_instances(
|
||||
&[inst],
|
||||
format!("unexpected token {t:?}; expected {expected}"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Import(pub Vec<String>);
|
||||
pub type Imports = HashSet<Import>;
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod riscv64;
|
||||
pub use super::*;
|
||||
@@ -1,216 +0,0 @@
|
||||
use super::{FnLowerCtx, Node, PAsmArg, PIdent, PInstruction};
|
||||
use crate::{
|
||||
compiler::arch::riscv::*,
|
||||
ir::{
|
||||
arch::riscv64::{RV64Instruction, RegRef},
|
||||
UIdent,
|
||||
},
|
||||
};
|
||||
|
||||
impl RV64Instruction {
|
||||
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
let args = &inst.args[..];
|
||||
let opstr = &**inst.op.inner.as_ref()?;
|
||||
// TODO: surely this can be abstracted...
|
||||
let opi = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3| -> Option<Self> {
|
||||
let [dest, src, imm] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Some(Self::OpImm { op, dest, src, imm })
|
||||
};
|
||||
let op = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||
let [dest, src1, src2] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src1 = RegRef::from_arg(src1, ctx)?;
|
||||
let src2 = RegRef::from_arg(src2, ctx)?;
|
||||
Some(Self::Op {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src1,
|
||||
src2,
|
||||
})
|
||||
};
|
||||
let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||
let [dest, src, imm] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Some(Self::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src,
|
||||
imm,
|
||||
})
|
||||
};
|
||||
let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||
let [src, offset, base] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let offset = i32_from_arg(offset, ctx)?;
|
||||
let base = RegRef::from_arg(base, ctx)?;
|
||||
Some(Self::Store {
|
||||
width,
|
||||
src,
|
||||
offset,
|
||||
base,
|
||||
})
|
||||
};
|
||||
let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||
let [dest, offset, base] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let offset = i32_from_arg(offset, ctx)?;
|
||||
let base = RegRef::from_arg(base, ctx)?;
|
||||
Some(Self::Load {
|
||||
width,
|
||||
dest,
|
||||
offset,
|
||||
base,
|
||||
})
|
||||
};
|
||||
Some(match opstr {
|
||||
"ecall" => Self::ECall,
|
||||
"li" => {
|
||||
let [dest, imm] = args else {
|
||||
ctx.err("li requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Self::Li { dest, imm }
|
||||
}
|
||||
"la" => {
|
||||
let [dest, src] = args else {
|
||||
ctx.err("la requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = arg_to_var(src, ctx)?;
|
||||
Self::La { dest, src }
|
||||
}
|
||||
"mv" => {
|
||||
let [dest, src] = args else {
|
||||
ctx.err("la requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
Self::Mv { dest, src }
|
||||
}
|
||||
|
||||
"lb" => load(ctx, width::B)?,
|
||||
"lh" => load(ctx, width::H)?,
|
||||
"lw" => load(ctx, width::W)?,
|
||||
"ld" => load(ctx, width::D)?,
|
||||
"lbu" => load(ctx, width::BU)?,
|
||||
"lhu" => load(ctx, width::HU)?,
|
||||
"lwu" => load(ctx, width::WU)?,
|
||||
|
||||
"sb" => store(ctx, width::B)?,
|
||||
"sh" => store(ctx, width::H)?,
|
||||
"sw" => store(ctx, width::W)?,
|
||||
"sd" => store(ctx, width::D)?,
|
||||
|
||||
"addi" => opi(ctx, op32i::ADD)?,
|
||||
"slti" => opi(ctx, op32i::SLT)?,
|
||||
"sltiu" => opi(ctx, op32i::SLTU)?,
|
||||
"xori" => opi(ctx, op32i::XOR)?,
|
||||
"ori" => opi(ctx, op32i::OR)?,
|
||||
"andi" => opi(ctx, op32i::AND)?,
|
||||
|
||||
"slli" => opif7(ctx, op32i::SL, op32i::LOGICAL)?,
|
||||
"srli" => opif7(ctx, op32i::SR, op32i::LOGICAL)?,
|
||||
"srla" => opif7(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
||||
|
||||
"add" => op(ctx, op32i::ADD, op32i::F7ADD)?,
|
||||
"sub" => op(ctx, op32i::ADD, op32i::F7SUB)?,
|
||||
"sll" => op(ctx, op32i::SL, op32i::FUNCT7)?,
|
||||
"slt" => op(ctx, op32i::SLT, op32i::FUNCT7)?,
|
||||
"sltu" => op(ctx, op32i::SLTU, op32i::FUNCT7)?,
|
||||
"xor" => op(ctx, op32i::XOR, op32i::FUNCT7)?,
|
||||
"srl" => op(ctx, op32i::SR, op32i::LOGICAL)?,
|
||||
"sra" => op(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
||||
"or" => op(ctx, op32i::OR, op32i::FUNCT7)?,
|
||||
"and" => op(ctx, op32i::AND, op32i::FUNCT7)?,
|
||||
|
||||
"mul" => op(ctx, op32m::MUL, op32m::FUNCT7)?,
|
||||
"mulh" => op(ctx, op32m::MULH, op32m::FUNCT7)?,
|
||||
"mulhsu" => op(ctx, op32m::MULHSU, op32m::FUNCT7)?,
|
||||
"mulhu" => op(ctx, op32m::MULHU, op32m::FUNCT7)?,
|
||||
"div" => op(ctx, op32m::DIV, op32m::FUNCT7)?,
|
||||
"divu" => op(ctx, op32m::DIVU, op32m::FUNCT7)?,
|
||||
"rem" => op(ctx, op32m::REM, op32m::FUNCT7)?,
|
||||
"remu" => op(ctx, op32m::REMU, op32m::FUNCT7)?,
|
||||
|
||||
w => {
|
||||
ctx.err_at(inst.op.origin, format!("Unknown instruction '{}'", w));
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg_to_var(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<UIdent> {
|
||||
let PAsmArg::Ref(node) = node.inner.as_ref()? else {
|
||||
ctx.err_at(
|
||||
node.origin,
|
||||
"Expected variable / function reference".to_string(),
|
||||
);
|
||||
return None;
|
||||
};
|
||||
ctx.ident(node)
|
||||
}
|
||||
|
||||
impl RegRef {
|
||||
pub fn from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
Some(match node.inner.as_ref()? {
|
||||
PAsmArg::Value(node) => {
|
||||
let reg = Reg::from_ident(node, ctx)?;
|
||||
Self::Reg(reg)
|
||||
}
|
||||
PAsmArg::Ref(node) => Self::Var(ctx.ident(node)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
pub fn from_ident(node: &Node<PIdent>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
let s = &**node.inner.as_ref()?;
|
||||
let res = Reg::from_str(s);
|
||||
if res.is_none() {
|
||||
ctx.err_at(node.origin, format!("Unknown reg name '{}'", s));
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn i32_from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<i32> {
|
||||
let PAsmArg::Value(node) = node.inner.as_ref()? else {
|
||||
ctx.err_at(node.origin, "Expected an i32, found reference".to_string());
|
||||
return None;
|
||||
};
|
||||
let word = node.inner.as_ref()?;
|
||||
match word.parse::<i32>() {
|
||||
Ok(x) => Some(x),
|
||||
Err(_) => {
|
||||
ctx.err_at(node.origin, format!("Expected an i64, found {}", word));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
use crate::{
|
||||
compiler::arch::riscv::Reg,
|
||||
ir::{
|
||||
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IdentID, Type, UInstruction,
|
||||
},
|
||||
parser::PAsmBlockArg,
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
|
||||
|
||||
type PLAsmBlockArg = PAsmBlockArg<Reg, IdentID>;
|
||||
|
||||
impl FnLowerable for PInstruction {
|
||||
type Output = RV64Instruction;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<RV64Instruction> {
|
||||
RV64Instruction::parse(self, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PAsmBlock {
|
||||
type Output = IdentID;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
let mut args = Vec::new();
|
||||
let mut output = None;
|
||||
for a in &self.args {
|
||||
if let Some(a) = a.lower(ctx) {
|
||||
match a {
|
||||
PAsmBlockArg::In { reg, var } => args.push(AsmBlockArg {
|
||||
reg,
|
||||
var,
|
||||
ty: AsmBlockArgType::In,
|
||||
}),
|
||||
PAsmBlockArg::Out { reg } => {
|
||||
if output.is_some() {
|
||||
ctx.err("cannot evaluate to more than one register".to_string());
|
||||
continue;
|
||||
}
|
||||
let var = ctx.temp(Type::Bits(64));
|
||||
args.push(AsmBlockArg {
|
||||
var,
|
||||
reg,
|
||||
ty: AsmBlockArgType::Out,
|
||||
});
|
||||
output = Some(var)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let block = UInstruction::AsmBlock {
|
||||
instructions: {
|
||||
let mut v = Vec::new();
|
||||
for i in &self.instructions {
|
||||
if let Some(i) = i.lower(ctx) {
|
||||
v.push(i);
|
||||
}
|
||||
}
|
||||
v
|
||||
},
|
||||
args,
|
||||
};
|
||||
ctx.push(block);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PUAsmBlockArg {
|
||||
type Output = PLAsmBlockArg;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
Some(match self {
|
||||
PAsmBlockArg::In { reg, var } => PLAsmBlockArg::In {
|
||||
reg: Reg::from_ident(reg, ctx)?,
|
||||
var: var.as_ref()?.lower(ctx)?,
|
||||
},
|
||||
PAsmBlockArg::Out { reg } => PLAsmBlockArg::Out {
|
||||
reg: Reg::from_ident(reg, ctx)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
use crate::{
|
||||
ir::{IdentID, Type, UIdent, UInstruction, UVar},
|
||||
parser::{PConstStatement, PStatementLike},
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
|
||||
|
||||
impl FnLowerable for PBlock {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
ctx.ident_stack.push();
|
||||
let mut last = None;
|
||||
let mut statements = Vec::new();
|
||||
let mut fn_nodes = Vec::new();
|
||||
let mut struct_nodes = Vec::new();
|
||||
let mut import_nodes = Vec::new();
|
||||
// first sort statements
|
||||
for s in &self.statements {
|
||||
let Some(s) = s.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
match s {
|
||||
PStatementLike::Statement(s) => statements.push(s),
|
||||
PStatementLike::Const(pconst_statement) => match pconst_statement {
|
||||
PConstStatement::Fn(f) => fn_nodes.push(f),
|
||||
PConstStatement::Struct(s) => struct_nodes.push(s),
|
||||
PConstStatement::Import(i) => import_nodes.push(i),
|
||||
},
|
||||
}
|
||||
}
|
||||
// then lower imports
|
||||
for i_n in &import_nodes {
|
||||
if let Some(i) = i_n.as_ref() {
|
||||
let name = &i.0;
|
||||
let path = ctx.path_for(name);
|
||||
let import = Import(path.clone());
|
||||
if ctx.imports.insert(import) {
|
||||
ctx.def_var(UVar {
|
||||
name: name.clone(),
|
||||
ty: Type::Module(path),
|
||||
origin: i_n.origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// then lower const things
|
||||
let mut structs = Vec::new();
|
||||
for s in &struct_nodes {
|
||||
structs.push(s.lower(ctx.ctx));
|
||||
}
|
||||
for (s, id) in struct_nodes.iter().zip(structs) {
|
||||
if let Some(id) = id {
|
||||
s.lower(ctx.ctx);
|
||||
}
|
||||
}
|
||||
let mut fns = Vec::new();
|
||||
for f in &fn_nodes {
|
||||
fns.push(f.lower(ctx.ctx));
|
||||
}
|
||||
for (f, id) in fn_nodes.iter().zip(fns) {
|
||||
if let Some(id) = id {
|
||||
f.lower(ctx.ctx);
|
||||
}
|
||||
}
|
||||
// then lower statements
|
||||
for s in statements {
|
||||
last = s.lower(ctx);
|
||||
}
|
||||
ctx.ident_stack.pop();
|
||||
last
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PStatement {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
match self {
|
||||
PStatement::Let(def, e) => {
|
||||
let def = def.lower(ctx.ctx)?;
|
||||
let res = e.lower(ctx);
|
||||
if let Some(res) = res {
|
||||
ctx.push(UInstruction::Mv { dst: def, src: res });
|
||||
}
|
||||
None
|
||||
}
|
||||
PStatement::Return(e) => {
|
||||
if let Some(e) = e {
|
||||
let src = e.lower(ctx)?;
|
||||
ctx.push_at(UInstruction::Ret { src }, src.origin);
|
||||
} else {
|
||||
let src = ctx.temp(Type::Unit);
|
||||
ctx.push_at(UInstruction::Ret { src }, src.origin);
|
||||
}
|
||||
None
|
||||
}
|
||||
PStatement::Expr(e) => e.lower(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ir::{UVar, VarID};
|
||||
|
||||
use super::{ModuleLowerCtx, Node, PVarDef};
|
||||
|
||||
impl Node<PVarDef> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<VarID> {
|
||||
let s = self.as_ref()?;
|
||||
let name = s.name.as_ref().map_or("{error}", |v| v).to_string();
|
||||
let ty = match &s.ty {
|
||||
Some(ty) => ty.lower(ctx),
|
||||
None => ctx.infer(),
|
||||
};
|
||||
Some(ctx.def_var(UVar {
|
||||
name,
|
||||
ty,
|
||||
origin: self.origin,
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
|
||||
use crate::{
|
||||
ir::{IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, Type, UData, UInstruction},
|
||||
parser::InfixOp,
|
||||
};
|
||||
|
||||
impl FnLowerable for PExpr {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
let mut e = self;
|
||||
let mut path = Vec::new();
|
||||
let mut gargs = None;
|
||||
loop {
|
||||
match e {
|
||||
PExpr::Member(node, ty, ident) => {
|
||||
e = if let Some(t) = node.as_ref() {
|
||||
ctx.origin = node.origin;
|
||||
path.push((ty, ident, gargs.unwrap_or_default()));
|
||||
&**t
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
}
|
||||
PExpr::Generic(node, nodes) => match gargs {
|
||||
None => gargs = Some(nodes.iter().map(|t| t.lower(ctx)).collect::<Vec<_>>()),
|
||||
Some(_) => {
|
||||
// this should cover the more specific area of ::<...>
|
||||
// but too lazy rn
|
||||
ctx.err("Cannot specify generics here".to_string());
|
||||
return None;
|
||||
}
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
while let PExpr::Member(node, ty, ident) = e {}
|
||||
if path.len() > 0 {
|
||||
// UIdent {
|
||||
// origin: ctx.origin,
|
||||
// status: IdentStatus::Unres { base: (), path: () },
|
||||
// }
|
||||
}
|
||||
let origin = ctx.origin;
|
||||
Some(match e {
|
||||
PExpr::Lit(l) => match l {
|
||||
super::PLiteral::String(s) => {
|
||||
let sty = Type::Bits(8).slice(ctx.p);
|
||||
let dst = ctx.temp_var(origin, sty);
|
||||
let data = s.as_bytes().to_vec();
|
||||
let dty = Type::Bits(8).arr(ctx.ctx.p, data.len() as u32);
|
||||
let dty = ctx.def_ty(dty);
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("string \"{}\"", s.replace("\n", "\\n")),
|
||||
ty: dty,
|
||||
content: data,
|
||||
});
|
||||
ctx.push(UInstruction::LoadSlice { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Char(c) => {
|
||||
let ty = ctx.def_ty(Type::Bits(8));
|
||||
let dst = ctx.temp_var(origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("char '{c}'"),
|
||||
ty,
|
||||
content: c.to_string().as_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Number(n) => {
|
||||
// TODO: temp
|
||||
let ty = ctx.def_ty(Type::Bits(64));
|
||||
let dst = ctx.temp_var(origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("num {n:?}"),
|
||||
ty,
|
||||
content: n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Unit => ctx.temp_var(origin, Type::Unit),
|
||||
},
|
||||
PExpr::Ident(i) => ctx.ident(i),
|
||||
PExpr::BinaryOp(op, e1, e2) => match op {
|
||||
InfixOp::Add => todo!(),
|
||||
InfixOp::Sub => todo!(),
|
||||
InfixOp::Mul => todo!(),
|
||||
InfixOp::Div => todo!(),
|
||||
InfixOp::LessThan => todo!(),
|
||||
InfixOp::GreaterThan => todo!(),
|
||||
InfixOp::Assign => {
|
||||
let res1 = e1.lower(ctx)?;
|
||||
let res2 = e2.lower(ctx)?;
|
||||
ctx.push(UInstruction::Mv {
|
||||
dst: res1,
|
||||
src: res2,
|
||||
});
|
||||
res1
|
||||
}
|
||||
},
|
||||
PExpr::PostfixOp(e, op) => {
|
||||
let res = e.lower(ctx)?;
|
||||
match op {
|
||||
PostfixOp::Ref => {
|
||||
let ty = Type::Ref(ctx.ctx.infer());
|
||||
let dest = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Ref {
|
||||
dst: dest,
|
||||
src: res,
|
||||
});
|
||||
dest
|
||||
}
|
||||
PostfixOp::Deref => {
|
||||
let ty = Type::Deref(ctx.ctx.infer());
|
||||
let dst = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Deref { dst, src: res });
|
||||
dst
|
||||
}
|
||||
PostfixOp::Not => todo!(),
|
||||
}
|
||||
}
|
||||
PExpr::Block(b) => b.lower(ctx)?,
|
||||
PExpr::AsmBlock(b) => b.lower(ctx)?,
|
||||
PExpr::Call(e, args) => {
|
||||
let fe = e.lower(ctx)?;
|
||||
let mut nargs = Vec::new();
|
||||
for arg in args.iter() {
|
||||
let arg = arg.lower(ctx)?;
|
||||
nargs.push(arg);
|
||||
}
|
||||
let dest = ctx.temp(Type::Infer);
|
||||
ctx.push(UInstruction::Call {
|
||||
dst: dest,
|
||||
f: fe,
|
||||
args: nargs,
|
||||
});
|
||||
dest
|
||||
}
|
||||
PExpr::Group(e) => e.lower(ctx)?,
|
||||
PExpr::Construct(e, map) => {
|
||||
let dst = ctx.temp(Type::Infer);
|
||||
let struc = e.lower(ctx)?;
|
||||
let fields = map.lower(ctx)?;
|
||||
ctx.push(UInstruction::Construct { dst, struc, fields });
|
||||
dst
|
||||
}
|
||||
PExpr::If(cond, body) => {
|
||||
let cond = cond.lower(ctx)?;
|
||||
ctx.ident_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.ident_stack.pop();
|
||||
ctx.push(UInstruction::If { cond, body });
|
||||
return None;
|
||||
}
|
||||
PExpr::Loop(body) => {
|
||||
ctx.ident_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.ident_stack.pop();
|
||||
ctx.push(UInstruction::Loop { body });
|
||||
return None;
|
||||
}
|
||||
PExpr::Break => {
|
||||
ctx.push(UInstruction::Break);
|
||||
return None;
|
||||
}
|
||||
PExpr::Continue => {
|
||||
ctx.push(UInstruction::Continue);
|
||||
return None;
|
||||
}
|
||||
PExpr::Member(e, ty, name) => {
|
||||
let id = e.lower(ctx)?;
|
||||
let name_str = name.as_ref()?.0;
|
||||
let cur = &mut ctx.p.idents[id];
|
||||
match cur.status {
|
||||
IdentStatus::Res(res) => {
|
||||
cur.status = IdentStatus::Unres {
|
||||
base: MemRes {
|
||||
mem: Member {
|
||||
id: MemberID
|
||||
},
|
||||
origin: (),
|
||||
gargs: (),
|
||||
},
|
||||
path: (),
|
||||
}
|
||||
}
|
||||
IdentStatus::Unres { base, path } => path.push(MemberIdent {
|
||||
ty: *ty,
|
||||
name: name_str,
|
||||
origin: name.origin,
|
||||
gargs: Vec::new(),
|
||||
}),
|
||||
IdentStatus::Failed(res_err) => return None,
|
||||
IdentStatus::Cooked => return None,
|
||||
}
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::{CompilerMsg, FileSpan, ModuleLowerCtx, Node, PFunction, Typable};
|
||||
use crate::{
|
||||
ir::{
|
||||
FnID, IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, MemberPath, MemberTy,
|
||||
Origin, Res, Type, UFunc, UIdent, UInstrInst, UInstruction,
|
||||
},
|
||||
parser,
|
||||
};
|
||||
|
||||
impl Node<PFunction> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<FnID> {
|
||||
self.as_ref().map(|s| s.lower(ctx, self.origin)).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl PFunction {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, origin: Origin) -> Option<FnID> {
|
||||
let header = self.header.as_ref()?;
|
||||
let name = header.name.as_ref()?.0.clone();
|
||||
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
|
||||
(
|
||||
header
|
||||
.gargs
|
||||
.iter()
|
||||
.flat_map(|a| a.lower(ctx).map(|g| (g.0, g.1, a.origin)))
|
||||
.collect(),
|
||||
header
|
||||
.args
|
||||
.iter()
|
||||
.flat_map(|a| Some(a.lower(ctx)?))
|
||||
.collect(),
|
||||
match &header.ret {
|
||||
Some(ty) => ty.lower(ctx),
|
||||
None => ctx.def_ty(Type::Unit),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), ctx.tc.error)
|
||||
};
|
||||
let gargs = generics.iter().map(|g| g.1).collect();
|
||||
let generics = generics
|
||||
.into_iter()
|
||||
.map(|g| {
|
||||
(
|
||||
g.0,
|
||||
ctx.def_ident(UIdent {
|
||||
status: IdentStatus::Res(Res::Generic(g.1)),
|
||||
origin: g.2,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
ctx.ident_stack.extend(generics.into_iter());
|
||||
let instructions = {
|
||||
let mut fctx = FnLowerCtx {
|
||||
instructions: Vec::new(),
|
||||
ctx,
|
||||
origin: self.body.origin,
|
||||
};
|
||||
let res = self.body.lower(&mut fctx);
|
||||
let mut instructions = fctx.instructions;
|
||||
if let Some(src) = res {
|
||||
let origin = ctx.idents[src].origin;
|
||||
instructions.push(UInstrInst {
|
||||
origin,
|
||||
i: UInstruction::Ret { src },
|
||||
});
|
||||
}
|
||||
instructions
|
||||
};
|
||||
let f = UFunc {
|
||||
origin,
|
||||
gargs,
|
||||
name,
|
||||
args,
|
||||
ret,
|
||||
instructions,
|
||||
};
|
||||
Some(ctx.def_fn(f))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FnLowerCtx<'a, 'b> {
|
||||
pub ctx: &'a mut ModuleLowerCtx<'b>,
|
||||
pub instructions: Vec<UInstrInst>,
|
||||
pub origin: FileSpan,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
pub fn ident(&mut self, node: &Node<parser::PIdent>) -> IdentID {
|
||||
let inst = UIdent {
|
||||
status: if let Some(n) = node.as_ref() {
|
||||
if let Some(&res) = self.ident_stack.search(&n.0) {
|
||||
return res;
|
||||
} else {
|
||||
IdentStatus::Unres {
|
||||
path: vec![MemberIdent {
|
||||
ty: MemberTy::Member,
|
||||
name: n.0.clone(),
|
||||
origin: node.origin,
|
||||
gargs: Vec::new(),
|
||||
}],
|
||||
base: MemRes {
|
||||
mem: Member {
|
||||
id: MemberID::Module(self.module),
|
||||
},
|
||||
origin: self.origin,
|
||||
gargs: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IdentStatus::Cooked
|
||||
},
|
||||
origin: node.origin,
|
||||
};
|
||||
self.def_ident(inst)
|
||||
}
|
||||
pub fn err(&mut self, msg: String) {
|
||||
let origin = self.origin;
|
||||
self.output.err(CompilerMsg::new(msg, origin))
|
||||
}
|
||||
pub fn err_at(&mut self, span: FileSpan, msg: String) {
|
||||
self.output.err(CompilerMsg::new(msg, span))
|
||||
}
|
||||
pub fn temp<T: Typable>(&mut self, ty: T) -> IdentID {
|
||||
self.ctx.temp_var(self.origin, ty)
|
||||
}
|
||||
pub fn push(&mut self, i: UInstruction) {
|
||||
self.push_at(i, self.origin);
|
||||
}
|
||||
pub fn push_at(&mut self, i: UInstruction, span: FileSpan) {
|
||||
self.instructions.push(UInstrInst { i, origin: span });
|
||||
}
|
||||
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
|
||||
FnLowerCtx {
|
||||
ctx: self.ctx,
|
||||
instructions: Vec::new(),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Deref for FnLowerCtx<'_, 'b> {
|
||||
type Target = ModuleLowerCtx<'b>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FnLowerCtx<'_, '_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FnLowerable {
|
||||
type Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T: FnLowerable> FnLowerable for Node<T> {
|
||||
type Output = T::Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
||||
let old_span = ctx.origin;
|
||||
ctx.origin = self.origin;
|
||||
let res = self.as_ref()?.lower(ctx);
|
||||
ctx.origin = old_span;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FnLowerable> FnLowerable for Box<T> {
|
||||
type Output = T::Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
||||
self.as_ref().lower(ctx)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ir::IdentID, parser::PMap};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl FnLowerable for PMap {
|
||||
type Output = HashMap<String, IdentID>;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
Some(
|
||||
self.0
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let expr = def.val.as_ref()?.lower(ctx)?;
|
||||
Some((name, expr))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
mod arch;
|
||||
mod asm;
|
||||
mod block;
|
||||
mod def;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod map;
|
||||
mod struc;
|
||||
mod ty;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
ir::{
|
||||
IdentID, IdentStatus, ModID, Origin, Res, Type, TypeID, UFunc, UIdent, UModule, UProgram,
|
||||
UVar,
|
||||
},
|
||||
util::NameStack,
|
||||
};
|
||||
pub use func::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl PModule {
|
||||
pub fn lower(
|
||||
&self,
|
||||
path: Vec<String>,
|
||||
p: &mut UProgram,
|
||||
imports: &mut Imports,
|
||||
output: &mut CompilerOutput,
|
||||
) -> ModID {
|
||||
let name = path.last().unwrap().clone();
|
||||
let f = UFunc {
|
||||
name: name.clone(),
|
||||
args: Vec::new(),
|
||||
instructions: Vec::new(),
|
||||
gargs: Vec::new(),
|
||||
ret: p.def_ty(Type::Unit),
|
||||
origin: self.block.origin,
|
||||
};
|
||||
let fid = p.def_fn(f);
|
||||
let mid = p.def_module(UModule {
|
||||
name,
|
||||
members: HashMap::new(),
|
||||
parent: None,
|
||||
func: fid,
|
||||
});
|
||||
let mut ctx = ModuleLowerCtx {
|
||||
p,
|
||||
output,
|
||||
module: mid,
|
||||
temp: 0,
|
||||
ident_stack: NameStack::new(),
|
||||
};
|
||||
let mut fctx = FnLowerCtx {
|
||||
ctx: &mut ctx,
|
||||
instructions: Vec::new(),
|
||||
origin: self.block.origin,
|
||||
};
|
||||
self.block.lower(&mut fctx);
|
||||
p.fns[fid].instructions = fctx.instructions;
|
||||
mid
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModuleLowerCtx<'a> {
|
||||
pub p: &'a mut UProgram,
|
||||
pub output: &'a mut CompilerOutput,
|
||||
pub module: ModID,
|
||||
pub temp: usize,
|
||||
pub ident_stack: NameStack<IdentID>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleLowerCtx<'a> {
|
||||
pub fn new(program: &'a mut UProgram, output: &'a mut CompilerOutput, id: ModID) -> Self {
|
||||
Self {
|
||||
p: program,
|
||||
output,
|
||||
module: id,
|
||||
temp: 0,
|
||||
ident_stack: NameStack::new(),
|
||||
}
|
||||
}
|
||||
pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
|
||||
self.temp_var_inner(origin, ty)
|
||||
}
|
||||
fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
|
||||
let var = UVar {
|
||||
name: format!("temp{}", self.temp),
|
||||
ty: ty.ty(self),
|
||||
origin,
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
};
|
||||
let id = self.p.def_var(var);
|
||||
self.temp += 1;
|
||||
self.def_ident(UIdent {
|
||||
status: IdentStatus::Res(Res::Var(id)),
|
||||
origin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Typable {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID;
|
||||
}
|
||||
|
||||
impl Typable for Type {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
p.def_ty(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Typable for TypeID {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ModuleLowerCtx<'_> {
|
||||
type Target = UProgram;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ModuleLowerCtx<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
use crate::{
|
||||
common::FileSpan,
|
||||
ir::{StructField, StructID, UStruct},
|
||||
parser::{PStruct, PStructFields},
|
||||
};
|
||||
|
||||
use super::ModuleLowerCtx;
|
||||
|
||||
impl PStruct {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, span: FileSpan) -> Option<StructID> {
|
||||
ctx.ident_stack.push();
|
||||
let gmap: Vec<_> = self.generics.iter().flat_map(|a| a.lower(ctx)).collect();
|
||||
let gargs = gmap.iter().map(|(_, id)| *id).collect();
|
||||
let fields = match &self.fields {
|
||||
PStructFields::Named(nodes) => nodes
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let tynode = def.ty.as_ref()?;
|
||||
let ty = tynode.lower(ctx);
|
||||
Some((name, ty))
|
||||
})
|
||||
.collect(),
|
||||
PStructFields::Tuple(nodes) => nodes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, n)| {
|
||||
let ty = n.as_ref()?.lower(ctx, span);
|
||||
Some((format!("{i}"), ty))
|
||||
})
|
||||
.collect(),
|
||||
PStructFields::None => vec![],
|
||||
}
|
||||
.into_iter()
|
||||
.map(|(name, ty)| (name, StructField { ty }))
|
||||
.collect();
|
||||
let name = self.name.as_ref()?.to_string();
|
||||
ctx.ident_stack.pop();
|
||||
Some(ctx.def_struct(UStruct {
|
||||
name,
|
||||
gargs,
|
||||
fields,
|
||||
origin: span,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
use crate::{
|
||||
ir::{GenericID, MemberIdent, MemberPath, Type, TypeID, UGeneric, UProgram},
|
||||
parser::PGenericDef,
|
||||
};
|
||||
|
||||
use super::{FileSpan, ModuleLowerCtx, Node, PType};
|
||||
|
||||
impl Node<Box<PType>> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
|
||||
self.as_ref()
|
||||
.map(|t| t.lower(ctx, self.origin))
|
||||
.unwrap_or(ctx.tc.error)
|
||||
}
|
||||
}
|
||||
impl Node<PType> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
|
||||
self.as_ref()
|
||||
.map(|t| t.lower(ctx, self.origin))
|
||||
.unwrap_or(ctx.tc.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl PType {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, mut origin: FileSpan) -> TypeID {
|
||||
let mut ty = self;
|
||||
let mut path = Vec::new();
|
||||
while let PType::Member(node, ident) = ty {
|
||||
ty = if let Some(t) = node.as_ref() {
|
||||
let Some(name) = ident.as_ref() else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
origin = node.origin;
|
||||
path.push(MemberIdent {
|
||||
name: name.0.clone(),
|
||||
origin: ident.origin,
|
||||
});
|
||||
&**t
|
||||
} else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
}
|
||||
if !path.is_empty() {
|
||||
let PType::Ident(id) = ty else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
path.push(MemberIdent {
|
||||
name: id.0.clone(),
|
||||
origin,
|
||||
});
|
||||
path.reverse();
|
||||
let ty = Type::Unres(MemberPath {
|
||||
id: ctx.module,
|
||||
path,
|
||||
});
|
||||
return ctx.def_ty(ty);
|
||||
}
|
||||
let ty = match ty {
|
||||
PType::Member(_, _) => unreachable!(),
|
||||
PType::Ident(node) => {
|
||||
path.push(MemberIdent {
|
||||
name: node.0.clone(),
|
||||
origin,
|
||||
});
|
||||
path.reverse();
|
||||
Type::Unres(MemberPath {
|
||||
id: ctx.module,
|
||||
path,
|
||||
})
|
||||
}
|
||||
PType::Ref(node) => node.lower(ctx).rf(),
|
||||
PType::Generic(node, nodes) => todo!(),
|
||||
};
|
||||
ctx.def_ty(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<PGenericDef> {
|
||||
pub fn lower(&self, p: &mut UProgram) -> Option<(String, GenericID)> {
|
||||
let s = self.as_ref()?;
|
||||
let name = s.name.as_ref()?.to_string();
|
||||
Some((
|
||||
name.clone(),
|
||||
p.def_generic(UGeneric {
|
||||
name,
|
||||
origin: self.origin,
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
mod ctx;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod lower;
|
||||
mod node;
|
||||
mod nodes;
|
||||
mod parse;
|
||||
mod token;
|
||||
mod import;
|
||||
|
||||
use crate::common::{CompilerMsg, CompilerOutput, FileSpan, FilePos};
|
||||
pub use ctx::*;
|
||||
pub use cursor::*;
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
pub use parse::*;
|
||||
pub use token::*;
|
||||
pub use import::*;
|
||||
|
||||
// idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct
|
||||
// creation, etc. instead of specializing at the parsing level
|
||||
@@ -1,54 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::ir::Origin;
|
||||
|
||||
pub struct Node<T> {
|
||||
pub inner: Option<T>,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub fn new(inner: T, span: Origin) -> Self {
|
||||
Self {
|
||||
inner: Some(inner),
|
||||
origin: span,
|
||||
}
|
||||
}
|
||||
pub fn bx(self) -> Node<Box<T>> {
|
||||
Node {
|
||||
inner: self.inner.map(|v| Box::new(v)),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
pub fn map<T2, F: Fn(T) -> T2>(self, f: F) -> Node<T2> {
|
||||
Node {
|
||||
inner: self.inner.map(f),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Node<T> {
|
||||
type Target = Option<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Node<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.inner {
|
||||
Some(v) => v.fmt(f),
|
||||
None => f.write_str("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, Node, PExpr, PIdent, PInstruction, Parsable, ParseResult, ParserCtx, Symbol,
|
||||
};
|
||||
|
||||
pub struct PAsmBlock {
|
||||
pub instructions: Vec<Node<PInstruction>>,
|
||||
pub args: Vec<Node<PUAsmBlockArg>>,
|
||||
}
|
||||
|
||||
pub enum PAsmBlockArg<R, V> {
|
||||
In { reg: R, var: V },
|
||||
Out { reg: R },
|
||||
}
|
||||
|
||||
pub type PUAsmBlockArg = PAsmBlockArg<Node<PIdent>, Node<PExpr>>;
|
||||
|
||||
impl Parsable for PAsmBlock {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let args = if ctx.expect_peek()?.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
parse_list(ctx, Symbol::CloseParen)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let mut instructions = Vec::new();
|
||||
while !ctx.expect_peek()?.is_symbol(Symbol::CloseCurly) {
|
||||
let res = ctx.parse();
|
||||
instructions.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_sym_on_line(Symbol::CloseCurly);
|
||||
}
|
||||
}
|
||||
ctx.expect_sym(Symbol::CloseCurly)?;
|
||||
ParseResult::Ok(Self { instructions, args })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PUAsmBlockArg {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let reg = ctx.parse::<PIdent>()?;
|
||||
ParseResult::Ok(if reg.inner.as_ref().is_some_and(|s| s.0 == "out") {
|
||||
ctx.expect_sym(Symbol::Equals)?;
|
||||
let reg = ctx.parse()?;
|
||||
Self::Out { reg }
|
||||
} else {
|
||||
ctx.expect_sym(Symbol::Equals)?;
|
||||
let var = ctx.parse()?;
|
||||
Self::In { reg, var }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PAsmBlock {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("asm {")?;
|
||||
for i in &self.instructions {
|
||||
f.write_str("\n ")?;
|
||||
i.fmt(f)?;
|
||||
}
|
||||
if !self.instructions.is_empty() {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, PAsmBlock, PIdent, Keyword, Node, Parsable, ParseResult, ParserCtx, Symbol, PType, PVarDef,
|
||||
};
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub struct AsmFunctionHeader {
|
||||
// pub name: Node<Ident>,
|
||||
// pub args: Vec<Node<AsmVarDef>>,
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct AsmFunction {
|
||||
// pub header: Node<AsmFunctionHeader>,
|
||||
// pub body: Node<AsmBlock>,
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmFunctionHeader {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// ctx.expect_kw(Keyword::Asm)?;
|
||||
// ctx.expect_kw(Keyword::Fn)?;
|
||||
// let name = ctx.parse()?;
|
||||
// ctx.expect_sym(Symbol::OpenParen)?;
|
||||
// let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
// ParseResult::Ok(Self { name, args })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmFunction {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// let header = ctx.parse()?;
|
||||
// let body = ctx.parse()?;
|
||||
// ParseResult::Ok(Self { header, body })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub struct AsmVarDef {
|
||||
// pub reg: Node<Ident>,
|
||||
// pub name: Node<Ident>,
|
||||
// pub ty: Option<Node<Type>>,
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmVarDef {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// let reg = ctx.parse()?;
|
||||
// let name = ctx.parse()?;
|
||||
// if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
// ctx.next();
|
||||
// ctx.parse().map(|ty| Self { reg, name, ty: Some(ty) })
|
||||
// } else {
|
||||
// ParseResult::Ok(Self { reg, name, ty: None })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl std::fmt::Debug for AsmVarDef {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// self.name.fmt(f)?;
|
||||
// if let Some(ty) = &self.ty {
|
||||
// write!(f, ": {:?}", ty)?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
@@ -1,94 +0,0 @@
|
||||
use super::{CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol};
|
||||
|
||||
pub struct PInstruction {
|
||||
pub op: Node<PIdent>,
|
||||
pub args: Vec<Node<PAsmArg>>,
|
||||
}
|
||||
|
||||
pub enum PAsmArg {
|
||||
Value(PIdent),
|
||||
Ref(Node<PIdent>),
|
||||
}
|
||||
|
||||
impl Parsable for PInstruction {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let op = ctx.parse()?;
|
||||
let mut args = Vec::new();
|
||||
if !ctx.next_on_new_line() {
|
||||
let arg = ctx.parse()?;
|
||||
args.push(arg);
|
||||
loop {
|
||||
if ctx.next_on_new_line() {
|
||||
break;
|
||||
}
|
||||
ctx.expect_sym(Symbol::Comma)?;
|
||||
let arg = ctx.parse()?;
|
||||
args.push(arg);
|
||||
}
|
||||
}
|
||||
ParseResult::Ok(Self { op, args })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PAsmArg {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
if let Some(ident) = ctx.maybe_parse() {
|
||||
return ParseResult::Wrap(ident.map(Self::Value));
|
||||
}
|
||||
|
||||
let mut next = ctx.expect_peek()?;
|
||||
if next.is_symbol(Symbol::Minus) {
|
||||
ctx.next();
|
||||
if let Some(mut ident) = ctx.maybe_parse::<PIdent>() {
|
||||
// TODO: this is so messed up
|
||||
if let Some(i) = ident.node.as_mut() {
|
||||
i.0.insert(0, '-')
|
||||
}
|
||||
return ParseResult::Wrap(ident.map(Self::Value));
|
||||
}
|
||||
next = ctx.expect_peek()?;
|
||||
}
|
||||
|
||||
if !next.is_symbol(Symbol::OpenCurly) {
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(
|
||||
next,
|
||||
"An identifier or {identifier}",
|
||||
));
|
||||
}
|
||||
ctx.next();
|
||||
|
||||
let res = ctx.parse();
|
||||
if res.recover {
|
||||
ctx.seek_sym(Symbol::CloseCurly);
|
||||
return ParseResult::SubErr;
|
||||
}
|
||||
|
||||
ctx.expect_sym(Symbol::CloseCurly)?;
|
||||
ParseResult::Ok(Self::Ref(res.node))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PAsmArg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Value(v) => v.fmt(f),
|
||||
Self::Ref(r) => write!(f, "{{{:?}}}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PInstruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.op.fmt(f)?;
|
||||
let mut iter = self.args.iter();
|
||||
if let Some(arg) = iter.next() {
|
||||
f.write_str(" ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
for arg in iter {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
token::Symbol, CompilerMsg, Node, NodeParsable, PStatementLike, ParseResult, ParserCtx,
|
||||
};
|
||||
use crate::{
|
||||
parser::{ParsableWith, TokenInstance},
|
||||
util::Padder,
|
||||
};
|
||||
|
||||
pub struct PBlock {
|
||||
pub statements: Vec<Node<PStatementLike>>,
|
||||
pub ret_last: bool,
|
||||
}
|
||||
|
||||
impl ParsableWith for PBlock {
|
||||
type Data = Option<Symbol>;
|
||||
fn parse(ctx: &mut ParserCtx, end: Option<Symbol>) -> ParseResult<Self> {
|
||||
let mut statements = Vec::new();
|
||||
let is_end = |t: &TokenInstance| -> bool { end.map(|e| t.is_symbol(e)).unwrap_or(false) };
|
||||
if ctx.peek().is_none_or(is_end) {
|
||||
ctx.next();
|
||||
return ParseResult::Ok(Self {
|
||||
statements,
|
||||
ret_last: false,
|
||||
});
|
||||
}
|
||||
let mut expect_semi = false;
|
||||
let mut recover = false;
|
||||
loop {
|
||||
let Some(next) = ctx.peek() else {
|
||||
if end.is_some() {
|
||||
recover = true;
|
||||
ctx.err(CompilerMsg::unexpected_end());
|
||||
}
|
||||
break;
|
||||
};
|
||||
if is_end(next) {
|
||||
ctx.next();
|
||||
break;
|
||||
}
|
||||
if next.is_symbol(Symbol::Semicolon) {
|
||||
ctx.next();
|
||||
expect_semi = false;
|
||||
continue;
|
||||
} else if expect_semi {
|
||||
ctx.err(CompilerMsg {
|
||||
msg: "expected ';'".to_string(),
|
||||
spans: vec![ctx.next_start().char_span()],
|
||||
});
|
||||
}
|
||||
let res = PStatementLike::parse_node(ctx);
|
||||
expect_semi = res
|
||||
.node
|
||||
.as_ref()
|
||||
.is_some_and(|s| matches!(s, PStatementLike::Statement(..)));
|
||||
statements.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
|
||||
if ctx.peek().is_none() {
|
||||
recover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseResult::from_recover(
|
||||
Self {
|
||||
statements,
|
||||
ret_last: expect_semi,
|
||||
},
|
||||
recover,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PBlock {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if !self.statements.is_empty() {
|
||||
f.write_str("{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
let mut end = self.statements.len();
|
||||
if self.ret_last {
|
||||
end -= 1;
|
||||
}
|
||||
for i in 0..end {
|
||||
let s = &self.statements[i];
|
||||
// they don't expose wrap_buf :grief:
|
||||
padder.write_str(&format!("{s:?};\n"))?;
|
||||
}
|
||||
if self.ret_last
|
||||
&& let Some(s) = self.statements.last()
|
||||
{
|
||||
padder.write_str(&format!("{s:?}\n"))?;
|
||||
}
|
||||
f.write_char('}')?;
|
||||
} else {
|
||||
f.write_str("{}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{
|
||||
CompilerMsg, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx,
|
||||
Symbol, Token,
|
||||
};
|
||||
|
||||
pub struct PVarDef {
|
||||
pub name: Node<PIdent>,
|
||||
pub ty: Option<Node<PType>>,
|
||||
}
|
||||
|
||||
pub struct PFieldDef {
|
||||
pub name: Node<PIdent>,
|
||||
pub val: Option<Node<PExpr>>,
|
||||
}
|
||||
|
||||
impl Parsable for PVarDef {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
ctx.next();
|
||||
Self {
|
||||
name,
|
||||
ty: Some(ctx.parse()?),
|
||||
}
|
||||
} else {
|
||||
Self { name, ty: None }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PFieldDef {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
ctx.next();
|
||||
Self {
|
||||
name,
|
||||
val: Some(ctx.parse()?),
|
||||
}
|
||||
} else {
|
||||
Self { name, val: None }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelfVar {
|
||||
pub ty: SelfType,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum SelfType {
|
||||
Ref,
|
||||
Take,
|
||||
}
|
||||
|
||||
// impl Parsable for Option<SelfVar> {
|
||||
// fn maybe_parse(ctx: &mut ParserCtx) -> Result<Option<Self>, CompilerMsg> {
|
||||
// if let Some(mut next) = ctx.peek() {
|
||||
// let mut ty = SelfType::Take;
|
||||
// if next.is_symbol(Symbol::Ampersand) {
|
||||
// ctx.next();
|
||||
// ty = SelfType::Ref;
|
||||
// next = ctx.expect_peek()?;
|
||||
// }
|
||||
// if let Token::Word(name) = &next.token {
|
||||
// if name == "self" {
|
||||
// ctx.next();
|
||||
// return Ok(Some(Self { ty }));
|
||||
// }
|
||||
// }
|
||||
// if ty != SelfType::Take {
|
||||
// return Err(CompilerMsg::unexpected_token(next, "self"));
|
||||
// }
|
||||
// }
|
||||
// Ok(None)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Debug for PVarDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.name.fmt(f)?;
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, ": {:?}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PFieldDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.name.fmt(f)?;
|
||||
if let Some(val) = &self.val {
|
||||
write!(f, ": {:?}", val)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SelfVar {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self.ty {
|
||||
SelfType::Ref => "&self",
|
||||
SelfType::Take => "self",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use crate::{common::FilePos, ir::MemberTy, parser::NodeParsableWith};
|
||||
|
||||
use super::{
|
||||
op::{InfixOp, PostfixOp},
|
||||
util::parse_list,
|
||||
CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, PType, Parsable,
|
||||
ParseResult, ParserCtx, Symbol,
|
||||
};
|
||||
|
||||
type BoxNode = Node<Box<PExpr>>;
|
||||
|
||||
pub enum PExpr {
|
||||
Lit(PLiteral),
|
||||
Ident(Node<PIdent>),
|
||||
BinaryOp(InfixOp, BoxNode, BoxNode),
|
||||
PostfixOp(BoxNode, PostfixOp),
|
||||
Block(Node<PBlock>),
|
||||
Call(BoxNode, Vec<Node<PExpr>>),
|
||||
Group(BoxNode),
|
||||
Member(BoxNode, MemberTy, Node<PIdent>),
|
||||
Generic(BoxNode, Vec<Node<PType>>),
|
||||
AsmBlock(Node<PAsmBlock>),
|
||||
Construct(BoxNode, Node<PMap>),
|
||||
If(BoxNode, BoxNode),
|
||||
Loop(BoxNode),
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl Parsable for PExpr {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let start = ctx.next_start();
|
||||
let mut e1 = Self::parse_unit_postfix(ctx)?;
|
||||
while let Some(op) = ctx
|
||||
.peek()
|
||||
.map(|next| InfixOp::from_token(&next.token))
|
||||
.flatten()
|
||||
{
|
||||
let span = start.to(ctx.prev_end());
|
||||
ctx.next();
|
||||
let n2 = ctx.parse()?.bx();
|
||||
let (n1, op, n2) = fix_precedence(Node::new(e1, span).bx(), op, n2, start);
|
||||
e1 = Self::BinaryOp(op, n1, n2);
|
||||
}
|
||||
return ParseResult::Ok(e1);
|
||||
}
|
||||
}
|
||||
|
||||
impl PExpr {
|
||||
fn parse_unit_postfix(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let start = ctx.next_start();
|
||||
// first get unit
|
||||
let mut e1 = Self::parse_unit(ctx)?;
|
||||
// then apply post ops
|
||||
loop {
|
||||
let span = start.to(ctx.prev_end());
|
||||
let Some(next) = ctx.peek() else {
|
||||
break;
|
||||
};
|
||||
if next.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
e1 = Self::Call(Node::new(e1, span).bx(), args);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
ctx.next();
|
||||
let map = ctx.parse()?;
|
||||
e1 = Self::Construct(Node::new(e1, span).bx(), map);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::Dot) {
|
||||
ctx.next();
|
||||
let field = ctx.parse()?;
|
||||
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Field, field);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::DoubleColon) {
|
||||
ctx.next();
|
||||
if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::OpenAngle)) {
|
||||
ctx.next();
|
||||
let gargs = parse_list(ctx, Symbol::CloseAngle)?;
|
||||
e1 = Self::Generic(Node::new(e1, span).bx(), gargs);
|
||||
} else {
|
||||
let field = ctx.parse()?;
|
||||
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Member, field);
|
||||
}
|
||||
continue;
|
||||
} else if let Some(op) = PostfixOp::from_token(next) {
|
||||
ctx.next();
|
||||
e1 = Self::PostfixOp(Node::new(e1, span).bx(), op);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ParseResult::Ok(e1);
|
||||
}
|
||||
fn parse_unit(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let next = ctx.expect_peek()?;
|
||||
return ParseResult::Ok(if next.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
if ctx.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||
ctx.next();
|
||||
return ParseResult::Ok(PExpr::Lit(PLiteral::Unit));
|
||||
}
|
||||
let res = ctx.parse();
|
||||
if res.recover {
|
||||
ctx.seek_sym(Symbol::CloseParen);
|
||||
}
|
||||
ctx.expect_sym(Symbol::CloseParen)?;
|
||||
Self::Group(res.node.bx())
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
ctx.next();
|
||||
Self::Block(PBlock::parse_node(ctx, Some(Symbol::CloseCurly))?)
|
||||
} else if next.is_keyword(Keyword::If) {
|
||||
ctx.next();
|
||||
let cond = ctx.parse()?.bx();
|
||||
let body = ctx.parse()?.bx();
|
||||
Self::If(cond, body)
|
||||
} else if next.is_keyword(Keyword::Loop) {
|
||||
ctx.next();
|
||||
let body = ctx.parse()?.bx();
|
||||
Self::Loop(body)
|
||||
} else if next.is_keyword(Keyword::Break) {
|
||||
ctx.next();
|
||||
Self::Break
|
||||
} else if next.is_keyword(Keyword::Continue) {
|
||||
ctx.next();
|
||||
Self::Continue
|
||||
} else if next.is_keyword(Keyword::Asm) {
|
||||
ctx.next();
|
||||
Self::AsmBlock(ctx.parse()?)
|
||||
} else if let Some(res) = ctx.maybe_parse::<PLiteral>() {
|
||||
return ParseResult::Wrap(res.map(Self::Lit));
|
||||
} else {
|
||||
let res = ctx.parse();
|
||||
if res.node.is_some() {
|
||||
Self::Ident(res.node)
|
||||
} else {
|
||||
let next = ctx.expect_peek()?;
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an expression"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fix_precedence(
|
||||
mut n1: BoxNode,
|
||||
mut op: InfixOp,
|
||||
mut n2: BoxNode,
|
||||
start: FilePos,
|
||||
) -> (BoxNode, InfixOp, BoxNode) {
|
||||
if let Some(box PExpr::BinaryOp(op2, _, _)) = n2.as_ref() {
|
||||
if op.precedence() > op2.precedence() {
|
||||
let Some(box PExpr::BinaryOp(op2, n21, n22)) = n2.inner else {
|
||||
unreachable!();
|
||||
};
|
||||
let span = start.to(n21.origin.end);
|
||||
let (n11, op1, n12) = fix_precedence(n1, op, n21, start);
|
||||
n1 = Node::new(PExpr::BinaryOp(op1, n11, n12), span).bx();
|
||||
op = op2;
|
||||
n2 = n22;
|
||||
}
|
||||
}
|
||||
(n1, op, n2)
|
||||
}
|
||||
|
||||
impl Debug for PExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PExpr::Lit(c) => c.fmt(f)?,
|
||||
PExpr::Ident(n) => n.fmt(f)?,
|
||||
PExpr::Block(b) => b.fmt(f)?,
|
||||
PExpr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
PExpr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
PExpr::PostfixOp(e, op) => write!(f, "({:?}{})", e, op.str())?,
|
||||
PExpr::Group(inner) => inner.fmt(f)?,
|
||||
PExpr::AsmBlock(inner) => inner.fmt(f)?,
|
||||
PExpr::Construct(node, inner) => write!(f, "{:?}{:?}", node, inner)?,
|
||||
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
|
||||
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
|
||||
PExpr::Break => write!(f, "break")?,
|
||||
PExpr::Continue => write!(f, "continue")?,
|
||||
PExpr::Member(e1, ty, name) => write!(f, "{:?}{}{:?}", e1, ty.sep(), name)?,
|
||||
PExpr::Generic(e1, gargs) => write!(f, "{:?}<{:?}>", e1, gargs)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, Node, PBlock, PGenericDef, PIdent, PType, PVarDef, Parsable, ParseResult,
|
||||
ParserCtx, Symbol,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct PFunctionHeader {
|
||||
pub name: Node<PIdent>,
|
||||
pub args: Vec<Node<PVarDef>>,
|
||||
pub gargs: Vec<Node<PGenericDef>>,
|
||||
pub ret: Option<Node<PType>>,
|
||||
}
|
||||
|
||||
pub struct PFunction {
|
||||
pub header: Node<PFunctionHeader>,
|
||||
pub body: Node<PBlock>,
|
||||
}
|
||||
|
||||
impl Parsable for PFunctionHeader {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
let generic_args = if ctx.expect_peek()?.is_symbol(Symbol::OpenAngle) {
|
||||
ctx.next();
|
||||
parse_list(ctx, Symbol::CloseAngle)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
ctx.expect_sym(Symbol::OpenParen)?;
|
||||
// let sel = ctx.maybe_parse();
|
||||
// if sel.is_some() {
|
||||
// if let Err(err) = ctx.expect_sym(Symbol::Comma) {
|
||||
// ctx.err(err);
|
||||
// ctx.seek_syms(&[Symbol::Comma, Symbol::CloseParen]);
|
||||
// if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Comma)) {
|
||||
// ctx.next();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
let ret = if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Arrow)) {
|
||||
ctx.next();
|
||||
Some(ctx.parse()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ParseResult::Ok(Self {
|
||||
name,
|
||||
args,
|
||||
gargs: generic_args,
|
||||
ret,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PFunction {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let header = ctx.parse()?;
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let body = ctx.parse_with(Some(Symbol::CloseCurly))?;
|
||||
ParseResult::Ok(Self { header, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PFunctionHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
self.name.fmt(f)?;
|
||||
f.write_str("(")?;
|
||||
// if let Some(s) = &self.sel {
|
||||
// s.fmt(f)?;
|
||||
// if self.args.first().is_some() {
|
||||
// f.write_str(", ")?;
|
||||
// }
|
||||
// }
|
||||
if let Some(a) = self.args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in self.args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_str(")")?;
|
||||
if let Some(ret) = &self.ret {
|
||||
write!(f, " -> {:?}", ret)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Debug for PFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.header.fmt(f)?;
|
||||
f.write_str(" ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::{CompilerMsg, Parsable, ParseResult, ParserCtx, Token};
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PIdent(pub String);
|
||||
|
||||
impl Parsable for PIdent {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let next = ctx.expect_peek()?;
|
||||
let Token::Word(name) = &next.token else {
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an identifier"));
|
||||
};
|
||||
let name = name.to_string();
|
||||
ctx.next();
|
||||
ParseResult::Ok(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for Option<PIdent> {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let Some(next) = ctx.peek() else {
|
||||
return ParseResult::Ok(None);
|
||||
};
|
||||
let Token::Word(name) = &next.token else {
|
||||
return ParseResult::Ok(None);
|
||||
};
|
||||
let name = name.to_string();
|
||||
ctx.next();
|
||||
ParseResult::Ok(Some(PIdent(name)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PIdent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PIdent {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PIdent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use crate::parser::{Parsable, ParseResult};
|
||||
|
||||
use super::{PString, ParserCtx, Symbol, Token};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum PLiteral {
|
||||
String(String),
|
||||
Char(char),
|
||||
Number(PNumber),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct PNumber {
|
||||
pub whole: String,
|
||||
pub decimal: Option<String>,
|
||||
pub ty: Option<String>,
|
||||
}
|
||||
|
||||
impl Parsable for Option<PLiteral> {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let inst = ctx.expect_peek()?;
|
||||
ParseResult::Ok(Some(match &inst.token {
|
||||
Token::Symbol(Symbol::SingleQuote) => {
|
||||
let chars = ctx.chars();
|
||||
let c = chars.expect_next()?;
|
||||
chars.expect('\'')?;
|
||||
ctx.next();
|
||||
PLiteral::Char(c)
|
||||
}
|
||||
Token::Symbol(Symbol::DoubleQuote) => {
|
||||
ctx.next();
|
||||
let s = ctx.parse::<PString>()?;
|
||||
return match s.inner {
|
||||
Some(s) => ParseResult::Ok(Some(PLiteral::String(s.0))),
|
||||
None => ParseResult::SubErr,
|
||||
};
|
||||
}
|
||||
Token::Word(text) => {
|
||||
let first = text.chars().next().unwrap();
|
||||
if !first.is_ascii_digit() {
|
||||
return ParseResult::Ok(None);
|
||||
}
|
||||
let (whole, ty) = parse_whole_num(text);
|
||||
let mut num = PNumber {
|
||||
whole,
|
||||
decimal: None,
|
||||
ty,
|
||||
};
|
||||
ctx.next();
|
||||
if num.ty.is_none() && ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Dot)) {
|
||||
ctx.next();
|
||||
if let Some(next) = ctx.peek() {
|
||||
if let Token::Word(i) = &next.token {
|
||||
if i.chars().next().unwrap().is_ascii_digit() {
|
||||
let (decimal, ty) = parse_whole_num(i);
|
||||
num.decimal = Some(decimal);
|
||||
num.ty = ty;
|
||||
ctx.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PLiteral::Number(num)
|
||||
}
|
||||
_ => return ParseResult::Ok(None),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_whole_num(text: &str) -> (String, Option<String>) {
|
||||
let mut whole = String::new();
|
||||
let mut ty = String::new();
|
||||
for c in text.chars() {
|
||||
if ty.is_empty() {
|
||||
if c.is_ascii_digit() {
|
||||
whole.push(c);
|
||||
} else if c != '_' {
|
||||
ty.push(c);
|
||||
}
|
||||
} else {
|
||||
ty.push(c);
|
||||
}
|
||||
}
|
||||
(whole, if ty.is_empty() { None } else { Some(ty) })
|
||||
}
|
||||
|
||||
impl Debug for PLiteral {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(str) => str.fmt(f),
|
||||
Self::Char(c) => c.fmt(f),
|
||||
Self::Number(n) => n.fmt(f),
|
||||
Self::Unit => f.write_str("()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PNumber {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.whole)?;
|
||||
if let Some(d) = &self.decimal {
|
||||
write!(f, ".{}", d)?;
|
||||
}
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, "_{}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
mod asm_block;
|
||||
mod asm_fn;
|
||||
mod asm_instr;
|
||||
mod block;
|
||||
mod def;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod ident;
|
||||
mod lit;
|
||||
mod op;
|
||||
mod statement;
|
||||
mod string;
|
||||
mod struc;
|
||||
mod trai;
|
||||
mod ty;
|
||||
mod util;
|
||||
|
||||
pub use asm_block::*;
|
||||
pub use asm_fn::*;
|
||||
pub use asm_instr::*;
|
||||
pub use block::*;
|
||||
pub use def::*;
|
||||
pub use expr::*;
|
||||
pub use func::*;
|
||||
pub use ident::*;
|
||||
pub use lit::*;
|
||||
pub use op::*;
|
||||
pub use statement::*;
|
||||
pub use struc::*;
|
||||
pub use trai::*;
|
||||
pub use ty::*;
|
||||
pub use string::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct PModule {
|
||||
pub block: Node<PBlock>,
|
||||
}
|
||||
|
||||
impl PModule {
|
||||
pub fn parse(ctx: &mut ParserCtx) -> Self {
|
||||
Self {
|
||||
block: PBlock::parse_node(ctx, None).node,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
use super::{Symbol, Token};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum InfixOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Assign,
|
||||
}
|
||||
|
||||
impl InfixOp {
|
||||
pub fn precedence(&self) -> u32 {
|
||||
match self {
|
||||
Self::Assign => 0,
|
||||
Self::LessThan => 1,
|
||||
Self::GreaterThan => 1,
|
||||
Self::Add => 2,
|
||||
Self::Sub => 3,
|
||||
Self::Mul => 4,
|
||||
Self::Div => 5,
|
||||
}
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::Assign => "=",
|
||||
}
|
||||
}
|
||||
pub fn pad(&self) -> bool {
|
||||
match self {
|
||||
Self::Add => true,
|
||||
Self::Sub => true,
|
||||
Self::Mul => true,
|
||||
Self::Div => true,
|
||||
Self::LessThan => true,
|
||||
Self::GreaterThan => true,
|
||||
Self::Assign => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PostfixOp {
|
||||
Not,
|
||||
Ref,
|
||||
Deref,
|
||||
}
|
||||
|
||||
impl InfixOp {
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::OpenAngle => Self::LessThan,
|
||||
Symbol::CloseAngle => Self::GreaterThan,
|
||||
Symbol::Plus => Self::Add,
|
||||
Symbol::Minus => Self::Sub,
|
||||
Symbol::Asterisk => Self::Mul,
|
||||
Symbol::Slash => Self::Div,
|
||||
Symbol::Equals => Self::Assign,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PostfixOp {
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Not => "!",
|
||||
Self::Ref => "@",
|
||||
Self::Deref => "^",
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::At => Self::Ref,
|
||||
Symbol::Bang => Self::Not,
|
||||
Symbol::Carrot => Self::Deref,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
use super::{
|
||||
Keyword, Node, PExpr, PFunction, PIdent, PStruct, PVarDef, Parsable, ParseResult, ParserCtx,
|
||||
Symbol, Token,
|
||||
};
|
||||
|
||||
pub enum PStatement {
|
||||
Let(Node<PVarDef>, Node<PExpr>),
|
||||
Return(Option<Node<PExpr>>),
|
||||
Expr(Node<PExpr>),
|
||||
}
|
||||
|
||||
pub enum PConstStatement {
|
||||
Fn(Node<PFunction>),
|
||||
Struct(Node<PStruct>),
|
||||
Import(Node<PIdent>),
|
||||
}
|
||||
|
||||
pub enum PStatementLike {
|
||||
Statement(PStatement),
|
||||
Const(PConstStatement),
|
||||
}
|
||||
|
||||
impl Parsable for PStatementLike {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let next = ctx.expect_peek()?;
|
||||
match next.token {
|
||||
Token::Keyword(Keyword::Let) => {
|
||||
ctx.next();
|
||||
let def = ctx.parse()?;
|
||||
ctx.expect_sym(Symbol::Equals)?;
|
||||
ctx.parse()
|
||||
.map_res(|expr| Self::Statement(PStatement::Let(def, expr)))
|
||||
}
|
||||
Token::Keyword(Keyword::Return) => {
|
||||
ctx.next();
|
||||
if ctx.peek().is_some_and(|t| t.is_symbol(Symbol::Semicolon)) {
|
||||
ParseResult::Ok(Self::Statement(PStatement::Return(None)))
|
||||
} else {
|
||||
ctx.parse()
|
||||
.map_res(|res| Self::Statement(PStatement::Return(Some(res))))
|
||||
}
|
||||
}
|
||||
Token::Keyword(Keyword::Fn) => {
|
||||
ctx.next();
|
||||
ParseResult::Ok(Self::Const(PConstStatement::Fn(ctx.parse()?)))
|
||||
}
|
||||
Token::Keyword(Keyword::Struct) => {
|
||||
ctx.next();
|
||||
ParseResult::Ok(Self::Const(PConstStatement::Struct(ctx.parse()?)))
|
||||
}
|
||||
Token::Keyword(Keyword::Import) => {
|
||||
ctx.next();
|
||||
ParseResult::Ok(Self::Const(PConstStatement::Import(ctx.parse()?)))
|
||||
}
|
||||
_ => ctx.parse().map_res(|n| Self::Statement(PStatement::Expr(n))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PStatement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PStatement::Let(n, e) => {
|
||||
f.write_str("let ")?;
|
||||
n.fmt(f)?;
|
||||
f.write_str(" = ")?;
|
||||
e.fmt(f)?;
|
||||
}
|
||||
PStatement::Return(e) => {
|
||||
f.write_str("return ")?;
|
||||
e.fmt(f)?;
|
||||
}
|
||||
PStatement::Expr(e) => {
|
||||
e.fmt(f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl std::fmt::Debug for PConstStatement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Fn(fun) => {
|
||||
fun.fmt(f)?;
|
||||
}
|
||||
Self::Struct(s) => {
|
||||
s.fmt(f)?;
|
||||
}
|
||||
Self::Import(s) => {
|
||||
writeln!(f, "import {:?}", s);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PStatementLike {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Statement(s) => s.fmt(f),
|
||||
Self::Const(c) => c.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
use crate::{
|
||||
common::CompilerMsg,
|
||||
parser::{Parsable, ParseResult},
|
||||
};
|
||||
|
||||
pub struct PString(pub String);
|
||||
|
||||
impl Parsable for PString {
|
||||
fn parse(ctx: &mut crate::parser::ParserCtx) -> ParseResult<Self> {
|
||||
let cursor = ctx.cursor.chars();
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let c = cursor.expect_next()?;
|
||||
if c == '"' {
|
||||
return ParseResult::Ok(Self(str));
|
||||
}
|
||||
str.push(match c {
|
||||
'\\' => {
|
||||
let start = cursor.prev_pos();
|
||||
let next = cursor.expect_next()?;
|
||||
match next {
|
||||
'"' => '"',
|
||||
'\'' => '\'',
|
||||
't' => '\t',
|
||||
'n' => '\n',
|
||||
'0' => '\0',
|
||||
other => {
|
||||
let end = cursor.prev_pos();
|
||||
ctx.output.err(CompilerMsg {
|
||||
msg: format!("Unknown escape sequence '\\{}'", other),
|
||||
spans: vec![start.to(end)],
|
||||
});
|
||||
other
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => c,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use crate::{parser::ParsableWith, util::Padder};
|
||||
|
||||
use super::{
|
||||
util::parse_list, CompilerMsg, Node, PFieldDef, PGenericDef, PIdent, PType, PVarDef, Parsable,
|
||||
ParseResult, ParserCtx, Symbol,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PStruct {
|
||||
pub name: Node<PIdent>,
|
||||
pub generics: Vec<Node<PGenericDef>>,
|
||||
pub fields: PStructFields,
|
||||
}
|
||||
|
||||
pub struct PMap(pub Vec<Node<PFieldDef>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PStructFields {
|
||||
Named(Vec<Node<PVarDef>>),
|
||||
Tuple(Vec<Node<PType>>),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Parsable for PStruct {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
let mut next = ctx.expect_peek()?;
|
||||
let args = if next.is_symbol(Symbol::OpenAngle) {
|
||||
ctx.next();
|
||||
let res = parse_list(ctx, Symbol::CloseAngle)?;
|
||||
next = ctx.expect_peek()?;
|
||||
res
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let fields = if next.is_symbol(Symbol::Semicolon) {
|
||||
ctx.next();
|
||||
PStructFields::None
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
ctx.next();
|
||||
PStructFields::Named(parse_list(ctx, Symbol::CloseCurly)?)
|
||||
} else if next.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
PStructFields::Tuple(parse_list(ctx, Symbol::CloseParen)?)
|
||||
} else {
|
||||
let msg = CompilerMsg::unexpected_token(next, "`;`, `(`, or `{`");
|
||||
ctx.err(msg);
|
||||
return ParseResult::Recover(PStruct {
|
||||
name,
|
||||
generics: args,
|
||||
fields: PStructFields::None,
|
||||
});
|
||||
};
|
||||
ParseResult::Ok(PStruct {
|
||||
name,
|
||||
generics: args,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PMap {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
ctx.expect_sym(Symbol::OpenCurly);
|
||||
ParseResult::Ok(Self(parse_list(ctx, Symbol::CloseCurly)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PMap {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "{{")?;
|
||||
let mut padder = Padder::new(f);
|
||||
for arg in &self.0 {
|
||||
padder.write_str(&format!("{arg:?},\n"))?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
use super::{
|
||||
util::{parse_list, parse_list_nosep},
|
||||
PFunction, PFunctionHeader, PIdent, Keyword, Node, Parsable, ParserCtx, Symbol, PType,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PTrait {
|
||||
pub name: Node<PIdent>,
|
||||
pub fns: Vec<Node<PFunctionHeader>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PImpl {
|
||||
pub trait_: Node<PType>,
|
||||
pub for_: Node<PType>,
|
||||
pub fns: Vec<Node<PFunction>>,
|
||||
}
|
||||
|
||||
impl Parsable for PTrait {
|
||||
fn parse(ctx: &mut ParserCtx) -> super::ParseResult<Self> {
|
||||
ctx.expect_kw(Keyword::Trait)?;
|
||||
let name = ctx.parse()?;
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let fns = parse_list(ctx, Symbol::CloseCurly)?;
|
||||
super::ParseResult::Ok(Self { name, fns })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PImpl {
|
||||
fn parse(ctx: &mut ParserCtx) -> super::ParseResult<Self> {
|
||||
ctx.expect_kw(Keyword::Impl)?;
|
||||
let trait_ = ctx.parse()?;
|
||||
ctx.expect_kw(Keyword::For)?;
|
||||
let for_ = ctx.parse()?;
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let fns = parse_list_nosep(ctx, Symbol::CloseCurly)?;
|
||||
super::ParseResult::Ok(Self { trait_, for_, fns })
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{util::parse_list, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol};
|
||||
|
||||
type BoxNode = Node<Box<PType>>;
|
||||
|
||||
pub enum PType {
|
||||
Member(BoxNode, Node<PIdent>),
|
||||
Ref(BoxNode),
|
||||
Generic(BoxNode, Vec<Node<PType>>),
|
||||
Ident(PIdent),
|
||||
}
|
||||
|
||||
pub struct PGenericDef {
|
||||
pub name: Node<PIdent>,
|
||||
}
|
||||
|
||||
impl Parsable for PType {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let start = ctx.next_start();
|
||||
let mut cur = ctx.parse()?.map(PType::Ident);
|
||||
loop {
|
||||
let span = start.to(ctx.prev_end());
|
||||
let Some(next) = ctx.peek() else {
|
||||
break;
|
||||
};
|
||||
if next.is_symbol(Symbol::Ampersand) {
|
||||
ctx.next();
|
||||
cur = Node::new(PType::Ref(cur.bx()), span);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::OpenAngle) {
|
||||
ctx.next();
|
||||
let args = parse_list(ctx, Symbol::CloseAngle)?;
|
||||
cur = Node::new(PType::Generic(cur.bx(), args), span);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::DoubleColon) {
|
||||
ctx.next();
|
||||
let mem = ctx.parse()?;
|
||||
cur = Node::new(PType::Member(cur.bx(), mem), span);
|
||||
}
|
||||
break;
|
||||
}
|
||||
ParseResult::Node(cur)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PGenericDef {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
ParseResult::Ok(Self { name: ctx.parse()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PType::Member(node, name) => write!(f, "{:?}.{:?}", node, name)?,
|
||||
PType::Ref(node) => write!(f, "{:?}&", node)?,
|
||||
PType::Generic(node, args) => write!(f, "{:?}<{:?}>", node, args)?,
|
||||
PType::Ident(node) => node.fmt(f)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PGenericDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::{Node, Parsable, ParserCtx, CompilerMsg, Symbol};
|
||||
|
||||
pub fn parse_list_sep<T: Parsable>(
|
||||
ctx: &mut ParserCtx,
|
||||
sep: Symbol,
|
||||
end: Symbol,
|
||||
) -> Result<Vec<Node<T>>, CompilerMsg> {
|
||||
let mut vals = Vec::new();
|
||||
loop {
|
||||
let next = ctx.expect_peek()?;
|
||||
if next.is_symbol(end) {
|
||||
break;
|
||||
}
|
||||
let res = ctx.parse();
|
||||
vals.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_syms(&[end, sep]);
|
||||
}
|
||||
let next = ctx.expect_peek()?;
|
||||
if !next.is_symbol(sep) {
|
||||
break;
|
||||
}
|
||||
ctx.next();
|
||||
}
|
||||
ctx.expect_sym(end)?;
|
||||
Ok(vals)
|
||||
}
|
||||
|
||||
pub fn parse_list<T: Parsable>(
|
||||
ctx: &mut ParserCtx,
|
||||
end: Symbol,
|
||||
) -> Result<Vec<Node<T>>, CompilerMsg> {
|
||||
parse_list_sep(ctx, Symbol::Comma, end)
|
||||
}
|
||||
|
||||
pub fn parse_list_nosep<T: Parsable>(
|
||||
ctx: &mut ParserCtx,
|
||||
end: Symbol,
|
||||
) -> Result<Vec<Node<T>>, CompilerMsg> {
|
||||
let mut vals = Vec::new();
|
||||
loop {
|
||||
let next = ctx.expect_peek()?;
|
||||
if next.is_symbol(end) {
|
||||
break;
|
||||
}
|
||||
let res = ctx.parse();
|
||||
vals.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_sym(end);
|
||||
}
|
||||
}
|
||||
ctx.expect_sym(end)?;
|
||||
Ok(vals)
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
};
|
||||
|
||||
use super::{CompilerMsg, FilePos, Node, ParserCtx};
|
||||
|
||||
pub enum ParseResult<T> {
|
||||
Ok(T),
|
||||
Wrap(NodeParseResult<T>),
|
||||
Node(Node<T>),
|
||||
Recover(T),
|
||||
Err(CompilerMsg),
|
||||
SubErr,
|
||||
}
|
||||
|
||||
impl<T> ParseResult<T> {
|
||||
pub fn from_recover(data: T, recover: bool) -> Self {
|
||||
if recover {
|
||||
Self::Recover(data)
|
||||
} else {
|
||||
Self::Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for ParseResult<T> {
|
||||
type Output = T;
|
||||
type Residual = Option<CompilerMsg>;
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self::Ok(output)
|
||||
}
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
ParseResult::Ok(v) => ControlFlow::Continue(v),
|
||||
// TODO: this is messed up; need to break w a Result<Option<T>> or smth :woozy:
|
||||
ParseResult::Recover(v) => ControlFlow::Break(None),
|
||||
ParseResult::Wrap(n) => {
|
||||
if n.recover {
|
||||
ControlFlow::Break(None)
|
||||
} else {
|
||||
match n.node.inner {
|
||||
Some(v) => ControlFlow::Continue(v),
|
||||
None => ControlFlow::Break(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseResult::Node(n) => match n.inner {
|
||||
Some(v) => ControlFlow::Continue(v),
|
||||
None => ControlFlow::Break(None),
|
||||
},
|
||||
ParseResult::Err(e) => ControlFlow::Break(Some(e)),
|
||||
ParseResult::SubErr => ControlFlow::Break(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for ParseResult<T> {
|
||||
fn from_residual(residual: <Self as Try>::Residual) -> Self {
|
||||
match residual {
|
||||
Some(err) => Self::Err(err),
|
||||
None => Self::SubErr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual<Result<Infallible, CompilerMsg>> for ParseResult<T> {
|
||||
fn from_residual(residual: Result<Infallible, CompilerMsg>) -> Self {
|
||||
match residual {
|
||||
Err(e) => Self::Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> FromResidual<ParseResult<T>> for ParseResult<U> {
|
||||
fn from_residual(residual: ParseResult<T>) -> Self {
|
||||
match residual {
|
||||
ParseResult::Err(e) => Self::Err(e),
|
||||
ParseResult::SubErr => Self::SubErr,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeParseResult<T> {
|
||||
pub node: Node<T>,
|
||||
pub recover: bool,
|
||||
}
|
||||
|
||||
impl<T> NodeParseResult<T> {
|
||||
pub fn map_res<F: FnOnce(Node<T>) -> U, U>(self, op: F) -> ParseResult<U> {
|
||||
let res = op(self.node);
|
||||
if self.recover {
|
||||
ParseResult::Recover(res)
|
||||
} else {
|
||||
ParseResult::Ok(res)
|
||||
}
|
||||
}
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, op: F) -> NodeParseResult<U> {
|
||||
NodeParseResult {
|
||||
node: Node {
|
||||
inner: self.node.inner.map(op),
|
||||
origin: self.node.origin,
|
||||
},
|
||||
recover: self.recover,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for NodeParseResult<T> {
|
||||
type Output = Node<T>;
|
||||
type Residual = ParseResult<T>;
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self {
|
||||
node: output,
|
||||
recover: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
if self.recover {
|
||||
ControlFlow::Break(ParseResult::SubErr)
|
||||
} else {
|
||||
ControlFlow::Continue(self.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for NodeParseResult<T> {
|
||||
fn from_residual(_: <Self as Try>::Residual) -> Self {
|
||||
// I hope this is unreachable ???
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Parsable: Sized {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self>;
|
||||
}
|
||||
|
||||
pub trait ParsableWith: Sized {
|
||||
type Data;
|
||||
|
||||
fn parse(ctx: &mut ParserCtx, data: Self::Data) -> ParseResult<Self>;
|
||||
}
|
||||
|
||||
impl<T: Parsable> ParsableWith for T {
|
||||
type Data = ();
|
||||
|
||||
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
|
||||
T::parse(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ParsableWith> Node<T> {
|
||||
pub fn parse_with(ctx: &mut ParserCtx, data: T::Data) -> NodeParseResult<T> {
|
||||
let start = ctx
|
||||
.peek()
|
||||
.map(|t| t.span.start)
|
||||
.unwrap_or(FilePos::start(ctx.cursor.file()));
|
||||
let (inner, recover) = match T::parse(ctx, data) {
|
||||
ParseResult::Ok(v) => (Some(v), false),
|
||||
ParseResult::Recover(v) => (Some(v), true),
|
||||
ParseResult::Wrap(r) => return r,
|
||||
ParseResult::Node(node) => {
|
||||
return NodeParseResult {
|
||||
node,
|
||||
recover: false,
|
||||
}
|
||||
}
|
||||
ParseResult::Err(e) => {
|
||||
ctx.err(e);
|
||||
(None, true)
|
||||
}
|
||||
ParseResult::SubErr => (None, true),
|
||||
};
|
||||
let end = ctx.prev_end();
|
||||
NodeParseResult {
|
||||
node: Self {
|
||||
inner,
|
||||
origin: start.to(end),
|
||||
},
|
||||
recover,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Parsable> Node<T> {
|
||||
pub fn parse(ctx: &mut ParserCtx) -> NodeParseResult<T> {
|
||||
Node::parse_with(ctx, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Node<Option<T>>
|
||||
where
|
||||
Option<T>: Parsable,
|
||||
{
|
||||
pub fn maybe_parse(ctx: &mut ParserCtx) -> Option<NodeParseResult<T>> {
|
||||
let res = Node::<Option<T>>::parse_with(ctx, ());
|
||||
let origin = res.node.origin;
|
||||
let recover = res.recover;
|
||||
match res.node.inner {
|
||||
Some(val) => match val {
|
||||
Some(v) => Some(NodeParseResult {
|
||||
node: Node {
|
||||
inner: Some(v),
|
||||
origin,
|
||||
},
|
||||
recover,
|
||||
}),
|
||||
None => None,
|
||||
},
|
||||
None => Some(NodeParseResult {
|
||||
node: Node {
|
||||
inner: None,
|
||||
origin,
|
||||
},
|
||||
recover,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeParsable {
|
||||
fn parse_node(ctx: &mut ParserCtx) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
impl<T: Parsable> NodeParsable for T {
|
||||
fn parse_node(ctx: &mut ParserCtx) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Node::<Self>::parse(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeParsableWith {
|
||||
type Data;
|
||||
fn parse_node(ctx: &mut ParserCtx, data: Self::Data) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
impl<T: ParsableWith<Data = D>, D> NodeParsableWith for T {
|
||||
type Data = D;
|
||||
fn parse_node(ctx: &mut ParserCtx, data: Self::Data) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Node::<Self>::parse_with(ctx, data)
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
use crate::common::FileID;
|
||||
|
||||
use super::super::{CompilerMsg, FilePos};
|
||||
|
||||
pub struct CharCursor<'a> {
|
||||
file: FileID,
|
||||
chars: Peekable<Chars<'a>>,
|
||||
next_pos: FilePos,
|
||||
prev_pos: FilePos,
|
||||
}
|
||||
|
||||
impl<'a> CharCursor<'a> {
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let res = self.peek()?;
|
||||
self.advance();
|
||||
Some(res)
|
||||
}
|
||||
pub fn expect(&mut self, c: char) -> Result<(), CompilerMsg> {
|
||||
let next = self.expect_next()?;
|
||||
if next == c {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CompilerMsg::at(
|
||||
self.prev_pos,
|
||||
format!("unexpected char '{next}'; expected '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
pub fn advance(&mut self) {
|
||||
let Some(next) = self.chars.next() else {
|
||||
return;
|
||||
};
|
||||
self.prev_pos = self.next_pos;
|
||||
if next == '\n' {
|
||||
self.next_pos.col = 0;
|
||||
self.next_pos.line += 1;
|
||||
} else {
|
||||
self.next_pos.col += 1;
|
||||
}
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<char, CompilerMsg> {
|
||||
self.next().ok_or(CompilerMsg::unexpected_end())
|
||||
}
|
||||
pub fn next_pos(&self) -> FilePos {
|
||||
self.next_pos
|
||||
}
|
||||
pub fn prev_pos(&self) -> FilePos {
|
||||
self.prev_pos
|
||||
}
|
||||
pub fn from_file_str(file: FileID, value: &'a str) -> Self {
|
||||
Self {
|
||||
chars: value.chars().peekable(),
|
||||
next_pos: FilePos::start(file),
|
||||
prev_pos: FilePos::start(file),
|
||||
file,
|
||||
}
|
||||
}
|
||||
pub fn file(&self) -> FileID {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Keyword {
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Return,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Struct,
|
||||
Trait,
|
||||
Impl,
|
||||
For,
|
||||
Asm,
|
||||
Import,
|
||||
Funne,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
pub fn from_string(str: &str) -> Option<Self> {
|
||||
Some(match str {
|
||||
"fn" => Self::Fn,
|
||||
"struct" => Self::Struct,
|
||||
"let" => Self::Let,
|
||||
"if" => Self::If,
|
||||
"for" => Self::For,
|
||||
"return" => Self::Return,
|
||||
"break" => Self::Break,
|
||||
"continue" => Self::Continue,
|
||||
"loop" => Self::Loop,
|
||||
"trait" => Self::Trait,
|
||||
"impl" => Self::Impl,
|
||||
"asm" => Self::Asm,
|
||||
"import" => Self::Import,
|
||||
"funne" => Self::Funne,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
mod cursor;
|
||||
mod keyword;
|
||||
mod symbol;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use super::FileSpan;
|
||||
pub use cursor::*;
|
||||
pub use keyword::*;
|
||||
pub use symbol::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Token {
|
||||
Symbol(Symbol),
|
||||
Word(String),
|
||||
Keyword(Keyword),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenInstance {
|
||||
pub token: Token,
|
||||
pub span: FileSpan,
|
||||
}
|
||||
|
||||
impl TokenInstance {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<TokenInstance> {
|
||||
cursor.skip_whitespace();
|
||||
cursor.peek()?;
|
||||
let start = cursor.next_pos();
|
||||
if let Some(s) = Symbol::parse(cursor) {
|
||||
if s == Symbol::DoubleSlash {
|
||||
while cursor.next() != Some('\n') {}
|
||||
return Self::parse(cursor);
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
return Some(Self {
|
||||
token: Token::Symbol(s),
|
||||
span: FileSpan {
|
||||
start,
|
||||
end,
|
||||
file: cursor.file(),
|
||||
},
|
||||
});
|
||||
}
|
||||
let mut word = String::new();
|
||||
while let Some(c) = cursor.peek() {
|
||||
if c.is_whitespace() || Symbol::from_char(c).is_some() {
|
||||
break;
|
||||
}
|
||||
word.push(c);
|
||||
cursor.advance();
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
let token = if let Some(keyword) = Keyword::from_string(&word) {
|
||||
Token::Keyword(keyword)
|
||||
} else {
|
||||
Token::Word(word)
|
||||
};
|
||||
Some(Self {
|
||||
token,
|
||||
span: FileSpan {
|
||||
start,
|
||||
end,
|
||||
file: cursor.file(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn is_symbol(&self, symbol: Symbol) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => *s == symbol,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_symbol_and(&self, f: impl Fn(Symbol) -> bool) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => f(*s),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyword(&self, kw: Keyword) -> bool {
|
||||
match self {
|
||||
Token::Keyword(k) => *k == kw,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TokenInstance {
|
||||
type Target = Token;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::CharCursor;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Symbol {
|
||||
Semicolon,
|
||||
Colon,
|
||||
DoubleColon,
|
||||
Equals,
|
||||
DoubleEquals,
|
||||
Arrow,
|
||||
DoubleArrow,
|
||||
Plus,
|
||||
Minus,
|
||||
Asterisk,
|
||||
Slash,
|
||||
DoubleSlash,
|
||||
Dot,
|
||||
DoubleDot,
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
OpenCurly,
|
||||
CloseCurly,
|
||||
OpenSquare,
|
||||
CloseSquare,
|
||||
OpenAngle,
|
||||
CloseAngle,
|
||||
SingleQuote,
|
||||
DoubleQuote,
|
||||
Bang,
|
||||
Ampersand,
|
||||
DoubleAmpersand,
|
||||
Pipe,
|
||||
DoublePipe,
|
||||
Comma,
|
||||
Hash,
|
||||
At,
|
||||
Carrot,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
||||
Self::from_char(cursor.peek()?).map(|mut s| {
|
||||
cursor.advance();
|
||||
s.finish(cursor);
|
||||
s
|
||||
})
|
||||
}
|
||||
pub fn from_char(c: char) -> Option<Self> {
|
||||
Some(match c {
|
||||
'(' => Self::OpenParen,
|
||||
')' => Self::CloseParen,
|
||||
'[' => Self::OpenSquare,
|
||||
']' => Self::CloseSquare,
|
||||
'{' => Self::OpenCurly,
|
||||
'}' => Self::CloseCurly,
|
||||
'<' => Self::OpenAngle,
|
||||
'>' => Self::CloseAngle,
|
||||
';' => Self::Semicolon,
|
||||
':' => Self::Colon,
|
||||
'+' => Self::Plus,
|
||||
'-' => Self::Minus,
|
||||
'*' => Self::Asterisk,
|
||||
'/' => Self::Slash,
|
||||
'=' => Self::Equals,
|
||||
'.' => Self::Dot,
|
||||
'\'' => Self::SingleQuote,
|
||||
'"' => Self::DoubleQuote,
|
||||
'!' => Self::Bang,
|
||||
'&' => Self::Ampersand,
|
||||
'|' => Self::Pipe,
|
||||
',' => Self::Comma,
|
||||
'#' => Self::Hash,
|
||||
'@' => Self::At,
|
||||
'^' => Self::Carrot,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
pub fn finish(&mut self, cursor: &mut CharCursor) {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return;
|
||||
};
|
||||
*self = match self {
|
||||
Self::Colon => match next {
|
||||
':' => Self::DoubleColon,
|
||||
_ => return,
|
||||
},
|
||||
Self::Minus => match next {
|
||||
'>' => Self::Arrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Equals => match next {
|
||||
'=' => Self::DoubleEquals,
|
||||
'>' => Self::DoubleArrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Slash => match next {
|
||||
'/' => Self::DoubleSlash,
|
||||
_ => return,
|
||||
},
|
||||
Self::Ampersand => match next {
|
||||
'&' => Self::DoubleAmpersand,
|
||||
_ => return,
|
||||
},
|
||||
Self::Pipe => match next {
|
||||
'&' => Self::DoublePipe,
|
||||
_ => return,
|
||||
},
|
||||
Self::Dot => match next {
|
||||
'.' => Self::DoubleDot,
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
cursor.advance();
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Semicolon => ";",
|
||||
Self::Colon => ":",
|
||||
Self::DoubleColon => "::",
|
||||
Self::Equals => "=",
|
||||
Self::DoubleEquals => "==",
|
||||
Self::Arrow => "->",
|
||||
Self::DoubleArrow => "=>",
|
||||
Self::Plus => "+",
|
||||
Self::Minus => "-",
|
||||
Self::Asterisk => "*",
|
||||
Self::Slash => "/",
|
||||
Self::DoubleSlash => "//",
|
||||
Self::Dot => ".",
|
||||
Self::DoubleDot => "..",
|
||||
Self::OpenParen => "(",
|
||||
Self::CloseParen => ")",
|
||||
Self::OpenCurly => "{",
|
||||
Self::CloseCurly => "}",
|
||||
Self::OpenSquare => "[",
|
||||
Self::CloseSquare => "]",
|
||||
Self::OpenAngle => "<",
|
||||
Self::CloseAngle => ">",
|
||||
Self::SingleQuote => "'",
|
||||
Self::DoubleQuote => "\"",
|
||||
Self::Bang => "!",
|
||||
Self::Comma => ",",
|
||||
Self::Ampersand => "&",
|
||||
Self::DoubleAmpersand => "&&",
|
||||
Self::Pipe => "|",
|
||||
Self::DoublePipe => "||",
|
||||
Self::Hash => "#",
|
||||
Self::At => "@",
|
||||
Self::Carrot => "^",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Symbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{}'", self.str())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user