1 Commits

Author SHA1 Message Date
iris 867f9e51bd parser2 2026-04-08 17:53:21 -04:00
45 changed files with 892 additions and 1277 deletions
Generated
+1 -1
View File
@@ -3,5 +3,5 @@
version = 4
[[package]]
name = "lang"
name = "v2"
version = "0.1.0"
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "lang"
name = "v2"
version = "0.1.0"
edition = "2024"
-121
View File
@@ -1,121 +0,0 @@
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub file: usize,
pub start: usize,
pub end: 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
}
}
pub struct CompilerMsg {
pub spans: Vec<Span>,
pub msg: String,
}
#[derive(Default)]
pub struct CompilerOutput {
pub errors: Vec<CompilerMsg>,
pub files: Vec<String>,
}
impl CompilerOutput {
pub fn new() -> Self {
Self::default()
}
pub fn error(&mut self, msg: impl Into<CompilerMsg>) {
self.errors.push(msg.into());
}
pub fn write(&self, w: &mut impl std::io::Write) {
let files: Vec<_> = self
.files
.iter()
.map(|path| std::fs::read_to_string(path).unwrap())
.collect();
for error in &self.errors {
writeln!(w, "Error: {}", error.msg).unwrap();
for span in &error.spans {
span.write(w, &files[span.file]).unwrap();
}
}
}
}
impl Span {
pub fn write(&self, w: &mut impl std::io::Write, text: &str) -> std::io::Result<()> {
let mut line_start = 0;
let mut found = false;
let mut line = 1;
let mut spans = Vec::new();
for (i, c) in text.char_indices() {
if i == self.start {
found = true;
}
if i == self.end {
found = true;
}
if c == '\n' {
if found {
spans.push((line, line_start..i));
}
line_start = i + 1;
line += 1;
found = false;
}
}
let underline = "\x1b[4:3m";
let underline_color = "\x1b[58;5;1m";
let end = "\x1b[0m";
if let [(line, range)] = &spans[..] {
writeln!(
w,
" {line:3} | {}{underline}{underline_color}{}{end}{}",
&text[range.start..self.start],
&text[self.start..=self.end],
&text[(self.end + 1)..range.end]
)?;
} else if let [(sline, srange), (eline, erange)] = &spans[..] {
writeln!(
w,
" {sline:3} | {}{underline}{underline_color}{}{end}",
&text[srange.start..self.start],
&text[self.start..=srange.end - 1],
)?;
if *eline != *sline + 1 {
writeln!(w, " ...")?;
}
writeln!(
w,
" {eline:3} | {underline}{underline_color}{}{end}{}",
&text[erange.start..=self.end],
&text[(self.end + 1)..=erange.end - 1],
)?;
}
Ok(())
}
}
impl From<String> for CompilerMsg {
fn from(value: String) -> Self {
Self {
spans: Vec::new(),
msg: value.to_string(),
}
}
}
-54
View File
@@ -1,54 +0,0 @@
use std::ops::{Index, IndexMut};
pub struct Id<T> {
idx: usize,
_pd: std::marker::PhantomData<T>,
}
pub struct IdVec<T> {
vec: Vec<T>,
}
impl<T> IdVec<T> {
pub fn add(&mut self, val: T) -> Id<T> {
let id = Id {
idx: self.vec.len(),
_pd: Default::default(),
};
self.vec.push(val);
id
}
}
impl<T> Index<Id<T>> for IdVec<T> {
type Output = T;
fn index(&self, index: Id<T>) -> &Self::Output {
&self.vec[index.idx]
}
}
impl<T> IndexMut<Id<T>> for IdVec<T> {
fn index_mut(&mut self, index: Id<T>) -> &mut Self::Output {
&mut self.vec[index.idx]
}
}
impl<T> Default for IdVec<T> {
fn default() -> Self {
Self {
vec: Default::default(),
}
}
}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
Self {
idx: self.idx.clone(),
_pd: self._pd.clone(),
}
}
}
impl<T> Copy for Id<T> {}
-23
View File
@@ -1,23 +0,0 @@
mod id;
mod structs;
pub use id::*;
pub use structs::*;
pub struct Ir {
pub root: Id<Namespace>,
pub namespaces: IdVec<Namespace>,
}
impl Ir {
pub fn root(&mut self) -> &mut Namespace {
&mut self.namespaces[self.root]
}
}
impl Default for Ir {
fn default() -> Self {
let mut namespaces = IdVec::default();
let root = namespaces.add(Namespace::default());
Self { root, namespaces }
}
}
-4
View File
@@ -1,4 +0,0 @@
mod namespace;
pub use namespace::*;
use super::Id;
-17
View File
@@ -1,17 +0,0 @@
use super::*;
use crate::parser::Ident;
use std::collections::HashMap;
#[derive(Default)]
pub struct Namespace {
pub items: HashMap<Ident, Item>,
}
pub enum Item {
Import(Id<Namespace>),
}
// issue: if I try to parse a function body, I'll want to have clear statements such as
// "call trait fn func on x" or "call field func of x", but you (often) can't tell until typed
// x.func
// x'func
+4 -20
View File
@@ -1,24 +1,8 @@
use crate::{
io::CompilerOutput,
parser::{Node, parse_file},
parser_ir::parse_program,
};
mod io;
mod ir;
#![feature(try_trait_v2)]
#![feature(associated_type_defaults)]
#![feature(trait_alias)]
mod parser;
mod parser_ir;
fn main() {
let mut args = std::env::args();
let Some(path) = args.nth(1) else {
println!("file expected");
return;
};
let mut output = CompilerOutput::new();
let root = parse_file(&path, &mut output);
if let Some(root) = root {
print!("{}", root.new_dsp());
}
output.write(&mut std::io::stdout());
parser::parse(include_str!("test.lang"));
}
+108
View File
@@ -0,0 +1,108 @@
use super::*;
pub struct ParserCtx<'a> {
pub cursor: TokenCursor<'a>,
pub msgs: &'a mut Vec<CompilerMsg>,
}
impl<'a> ParserCtx<'a> {
pub fn new(cursor: impl Into<TokenCursor<'a>>, msgs: &'a mut Vec<CompilerMsg>) -> Self {
Self {
cursor: cursor.into(),
msgs,
}
}
pub fn parse<T: Parsable<Data = ()>>(&mut self) -> Option<Node<T>> {
self.parse_with(())
}
pub fn parse_with<T: Parsable>(&mut self, data: T::Data) -> Option<Node<T>> {
let data = match T::parse(self, data) {
ParseResult::Ok(t) => Some(t),
ParseResult::Node(n) => return Some(n),
ParseResult::Break(msg) => {
self.msgs.push(msg);
return None;
}
ParseResult::Continue(msg) => {
self.msgs.push(msg);
None
}
ParseResult::SubErr => {
return None;
}
};
Some(Node { data })
}
pub fn seek(&mut self, token: impl Into<Token>) -> bool {
let token = token.into();
while let Some(next) = self.next() {
if next == token {
return true;
}
}
false
}
pub fn expect_next(&mut self) -> Option<Token> {
let res = self.cursor.next();
if res.is_none() {
self.msgs.push(CompilerMsg::new(
"Unexpected end of input",
self.cursor.prev_end(),
));
}
res
}
pub fn expect_peek(&self) -> Result<&Token, CompilerMsg> {
match self.cursor.peek() {
Some(t) => Ok(t),
None => Err(self.unexpected_end()),
}
}
pub fn expect(&mut self, token: impl Into<Token>) -> Result<(), CompilerMsg> {
let token = token.into();
if self.next_is_ref(&token) {
Ok(())
} else {
Err(self.unexpected(format!("token {:?}", token)))
}
}
fn unexpected_end(&self) -> CompilerMsg {
CompilerMsg::new("Unexpected end of input", self.next_start())
}
pub fn peek(&self) -> Option<&Token> {
self.cursor.peek()
}
pub fn unexpected<'b>(&self, expected: impl std::fmt::Display) -> CompilerMsg {
if let Some((next, span)) = self.peek_span() {
CompilerMsg::new(
format!("Unexpected token {:?}, expected {}", next, expected),
span,
)
} else {
self.unexpected_end()
}
}
}
impl<'a> std::ops::Deref for ParserCtx<'a> {
type Target = TokenCursor<'a>;
fn deref(&self) -> &Self::Target {
&self.cursor
}
}
impl<'a> std::ops::DerefMut for ParserCtx<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cursor
}
}
-38
View File
@@ -1,38 +0,0 @@
use super::Token;
use crate::io::Span;
pub struct Lit {
pub ty: LitTy,
pub span: Span,
}
#[derive(PartialEq)]
pub enum LitTy {
Number(String),
Bool(bool),
String(String),
Unit,
}
impl From<LitTy> for Token {
fn from(value: LitTy) -> Self {
Self::Lit(value)
}
}
impl std::fmt::Display for LitTy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Number(n) => write!(f, "{n}"),
Self::Bool(b) => write!(f, "{b}"),
Self::String(s) => write!(f, "\"{s}\""),
Self::Unit => write!(f, "()"),
}
}
}
impl std::fmt::Display for Lit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.ty.fmt(f)
}
}
-102
View File
@@ -1,102 +0,0 @@
use std::borrow::Borrow;
use crate::io::{CompilerMsg, Span, Spanned};
mod lit;
mod token;
pub use lit::*;
pub use token::*;
pub struct Cursor<'a> {
pub span: Span,
next: Option<TokenInst>,
tokens: Tokens<'a>,
}
impl<'a> Cursor<'a> {
pub fn new(text: &'a str, file: usize) -> Self {
let mut s = Self {
span: Span {
start: 0,
end: 0,
file,
},
next: None,
tokens: Tokens::new(text, file),
};
s.next();
s
}
pub fn next(&mut self) -> Option<Token> {
let mut next = self.tokens.next();
std::mem::swap(&mut self.next, &mut next);
next.map(|inst| {
self.span = inst.span;
inst.inner
})
}
pub fn next_if(&mut self, token: impl Borrow<Token>) -> bool {
if self.peek().is_some_and(|t| t == token.borrow()) {
self.next();
true
} else {
false
}
}
pub fn peek(&self) -> Option<&Token> {
self.next.as_ref().map(|i| &i.inner)
}
pub fn expect_next(&mut self) -> Result<Token, CompilerMsg> {
self.next().ok_or_else(CompilerMsg::unexpected_eof)
}
pub fn expect_peek(&self) -> Result<&Token, CompilerMsg> {
self.peek().ok_or_else(CompilerMsg::unexpected_eof)
}
pub fn expect(&mut self, token: impl Borrow<Token>) -> Result<Token, CompilerMsg> {
let token = token.borrow();
let next = self.expect_next()?;
if next == *token {
Ok(next)
} else {
self.unexpected(&next, &format!("'{token}'"))
}
}
pub fn unexpected<T>(&self, token: &Token, expected: &str) -> Result<T, CompilerMsg> {
Err(CompilerMsg::unexpected_token(token, self.span, expected))
}
pub fn peek_start(&mut self) -> usize {
self.next.as_ref().map(|i| i.span.start).unwrap_or(0)
}
pub fn cur_end(&mut self) -> usize {
self.span.end
}
pub fn file(&mut self) -> usize {
self.span.file
}
}
impl CompilerMsg {
pub fn unexpected_token(token: &Token, span: Span, expected: &str) -> Self {
Self {
spans: vec![span],
msg: format!("Unexpected token '{}', expected {expected}", token),
}
}
pub fn unexpected_eof() -> Self {
Self {
spans: Vec::new(),
msg: "unexpected end of file".to_string(),
}
}
}
-200
View File
@@ -1,200 +0,0 @@
use crate::parser::cursor::LitTy;
use super::{Span, Spanned};
use std::{iter::Peekable, str::CharIndices};
def_tokens! {
symbol {
Dot: ".",
Comma: ",",
Equal: "=",
Colon: ":",
Semicolon: ";",
Plus: "+",
Dash: "-",
Asterisk: "*",
Slash: "/",
OpenParen: "(",
CloseParen: ")",
OpenSquare: "[",
CloseSquare: "]",
OpenCurly: "{",
CloseCurly: "}",
Arrow: "->",
DoubleArrow: "=>",
PlusEqual: "+=",
DashEqual: "-=",
AsteriskEqual: "*=",
SlashEqual: "/=",
DoubleColon: "::",
Hash: "#",
}
keyword {
Let: "let",
Import: "import",
Fn: "fn",
If: "if",
Loop: "loop",
While: "while",
For: "for",
Match: "match",
}
other {
Ident(String),
Lit(LitTy),
}
}
pub type TokenInst = Spanned<Token>;
pub struct Tokens<'a> {
file: usize,
chars: Peekable<CharIndices<'a>>,
}
impl<'a> Tokens<'a> {
pub fn new(code: &'a str, file: usize) -> Self {
Self {
file,
chars: code.char_indices().peekable(),
}
}
}
impl Iterator for Tokens<'_> {
type Item = Spanned<Token>;
fn next(&mut self) -> Option<Self::Item> {
let (i, c) = self.chars.next()?;
let mut span = Span {
start: i,
end: i,
file: self.file,
};
if c.is_whitespace() {
return self.next();
}
macro_rules! then {
(_ => $def:expr, $($char:expr => $to:expr,)*) => {
match self.chars.peek() {
$(Some((_, $char)) => {
self.chars.next();
$to
},)*
_ => $def,
}
};
}
let inner = match c {
'.' => Token::Dot,
',' => Token::Comma,
'(' => Token::OpenParen,
')' => Token::CloseParen,
'[' => Token::OpenSquare,
']' => Token::CloseSquare,
'{' => Token::OpenCurly,
'}' => Token::CloseCurly,
'#' => Token::Hash,
'+' => then! {
_ => Token::Plus,
'=' => Token::PlusEqual,
},
'-' => then! {
_ => Token::Dash,
'=' => Token::DashEqual,
'>' => Token::Arrow,
},
'*' => then! {
_ => Token::Asterisk,
'=' => Token::AsteriskEqual,
},
'/' => then! {
_ => Token::Slash,
'=' => Token::SlashEqual,
},
':' => then! {
_ => Token::Colon,
':' => Token::DoubleColon,
},
';' => Token::Semicolon,
'=' => then! {
_ => Token::Equal,
'>' => Token::DoubleArrow,
},
'0'..='9' => {
let mut s = c.to_string();
while let Some((i, c)) = self.chars.peek()
&& c.is_alphanumeric()
{
s.push(*c);
span.end = *i;
self.chars.next();
}
LitTy::Number(s).into()
}
'"' => {
let mut s = String::new();
while let Some((i, c)) = self.chars.next()
&& !matches!(c, '"')
{
s.push(c);
span.end = i;
}
LitTy::String(s).into()
}
_ => {
let mut s = c.to_string();
while let Some((i, c)) = self.chars.peek()
&& c.is_alphanumeric()
{
s.push(*c);
span.end = *i;
self.chars.next();
}
match s.as_str() {
"true" => LitTy::Bool(true).into(),
"false" => LitTy::Bool(false).into(),
_ => from_str(s),
}
}
};
Some(Spanned { inner, span })
}
}
macro_rules! def_tokens {
{
symbol {
$($sym_name:ident: $sym_str:expr,)*
}
keyword {
$($kw_name:ident: $kw_str:expr,)*
}
other {
$($other_name:ident($data:ty),)*
}
} => {
#[derive(PartialEq)]
pub enum Token {
$($sym_name,)*
$($kw_name,)*
$($other_name($data),)*
}
fn from_str(s: String) -> Token {
match s.as_str() {
$($kw_str => Token::$kw_name,)*
_ => Token::Ident(s),
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(Token::$sym_name => write!(f, "{}", $sym_str),)*
$(Token::$kw_name => write!(f, $kw_str),)*
$(Token::$other_name(v) => write!(f, "{v}"),)*
}
}
}
};
}
use def_tokens;
+37
View File
@@ -0,0 +1,37 @@
use super::*;
#[derive(Debug)]
pub struct CompilerMsg {
msg: String,
span: CharSpan,
}
#[derive(Debug, Clone, Copy)]
pub struct CharSpan {
start: CharPos,
end: CharPos,
}
impl CharPos {
pub fn to(self, end: CharPos) -> CharSpan {
CharSpan { start: self, end }
}
}
impl CompilerMsg {
pub fn new(msg: impl Into<String>, span: impl Into<CharSpan>) -> Self {
Self {
msg: msg.into(),
span: span.into(),
}
}
}
impl From<CharPos> for CharSpan {
fn from(value: CharPos) -> Self {
Self {
start: value,
end: value,
}
}
}
+15 -25
View File
@@ -1,29 +1,19 @@
mod cursor;
mod node;
mod nodes;
mod ctx;
mod token;
mod tree;
mod io;
use cursor::*;
pub use node::*;
pub use nodes::*;
pub use ctx::*;
use token::*;
pub use tree::*;
pub use io::*;
use crate::io::CompilerOutput;
pub fn parse_file(path: &str, output: &mut CompilerOutput) -> Option<Body> {
let code = match std::fs::read_to_string(path) {
Ok(code) => code,
Err(err) => {
output.error(format!("Failed to read input file: {err}"));
return None;
pub fn parse(file: &str) {
let mut msgs = Vec::new();
let mut parser = ParserCtx::new(file, &mut msgs);
if let Some(block) = parser.parse_with::<PBlock>(false) {
println!("{block:#?}");
} else {
println!("{msgs:?}");
}
};
output.files.push(path.to_string());
let mut ctx = ParseCtx::new(Cursor::new(&code, 0));
let root = match ctx.parse() {
Ok(v) => v,
Err(msg) => {
output.error(msg);
return None;
}
};
Some(root)
}
-83
View File
@@ -1,83 +0,0 @@
use crate::{
io::{CompilerMsg, Span},
parser::{
Ident, Node,
cursor::{Cursor, Lit, LitTy, Token},
},
};
pub struct ParseCtx<'a> {
start: usize,
cursor: Cursor<'a>,
}
impl<'a> ParseCtx<'a> {
pub fn new(cursor: Cursor<'a>) -> Self {
Self { start: 0, cursor }
}
pub fn parse_box<N: Node>(&mut self) -> Result<Box<N>, CompilerMsg> {
self.parse_with(N::parse).map(Box::new)
}
pub fn parse<N: Node>(&mut self) -> Result<N, CompilerMsg> {
self.parse_with(N::parse)
}
pub fn parse_with<N: Node>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<N, CompilerMsg>,
) -> Result<N, CompilerMsg> {
let old_start = self.start;
self.start = self.cursor.peek_start();
let res = f(self).map(|r| r);
self.start = old_start;
res
}
pub fn ident(&mut self, s: String) -> Ident {
let span = self.cursor.span;
Ident { name: s, span }
}
pub fn lit(&mut self, ty: LitTy) -> Lit {
let span = self.cursor.span;
Lit { ty, span }
}
pub fn span(&mut self) -> Span {
let end = self.cursor.cur_end();
Span {
file: self.cursor.file(),
start: self.start,
end,
}
}
pub fn list<N: Node>(&mut self, sep: Token, end: Token) -> Result<Vec<N>, CompilerMsg> {
let mut list = Vec::new();
if self.next_if(&end) {
return Ok(list);
}
list.push(self.parse()?);
while self.next_if(&sep) {
list.push(self.parse()?);
}
self.expect(end)?;
Ok(list)
}
}
impl<'a> std::ops::Deref for ParseCtx<'a> {
type Target = Cursor<'a>;
fn deref(&self) -> &Self::Target {
&self.cursor
}
}
impl<'a> std::ops::DerefMut for ParseCtx<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cursor
}
}
-50
View File
@@ -1,50 +0,0 @@
use crate::parser::Node;
#[derive(Clone, Copy)]
pub struct DisplayCtx {
pub indent: usize,
}
pub struct NodeDsp<'a, N: Node> {
pub node: &'a N,
pub ctx: DisplayCtx,
}
impl<N: Node> std::fmt::Display for NodeDsp<'_, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.node.fmt(f, self.ctx)
}
}
pub struct VecDsp<'a, N> {
list: &'a Vec<N>,
ctx: DisplayCtx,
}
impl<N: Node> std::fmt::Display for VecDsp<'_, N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some((last, rest)) = self.list.split_last() {
for arg in rest {
write!(f, "{}, ", arg.dsp(self.ctx))?;
}
write!(f, "{}", last.dsp(self.ctx))?;
}
Ok(())
}
}
pub trait VecDspT<N> {
fn dsp<'a, 'b>(&'a self, ctx: impl Into<DisplayCtx>) -> VecDsp<'b, N>
where
'a: 'b;
}
impl<N> VecDspT<N> for Vec<N> {
fn dsp<'a, 'b>(&'a self, ctx: impl Into<DisplayCtx>) -> VecDsp<'b, N>
where
'a: 'b,
{
let ctx = ctx.into();
VecDsp { list: self, ctx }
}
}
-17
View File
@@ -1,17 +0,0 @@
mod ctx;
mod dsp;
pub use ctx::*;
pub use dsp::*;
use crate::io::CompilerMsg;
pub trait Node: Sized {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg>;
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result;
fn dsp(&self, ctx: DisplayCtx) -> NodeDsp<'_, Self> {
NodeDsp { node: self, ctx }
}
fn new_dsp(&self) -> NodeDsp<'_, Self> {
self.dsp(DisplayCtx { indent: 0 })
}
}
-58
View File
@@ -1,58 +0,0 @@
use super::*;
pub struct Body {
pub items: Vec<Item>,
pub final_semicolon: bool,
pub span: Span,
}
impl Node for Body {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut items = Vec::new();
fn at_end(ctx: &mut ParseCtx) -> bool {
ctx.peek().is_none_or(|t| *t == Token::CloseCurly)
}
let final_semicolon = loop {
if at_end(ctx) {
break true;
}
let item: Item = ctx.parse()?;
let needs_semicolon = item.needs_semicolon();
items.push(item);
if at_end(ctx) {
break false;
}
if needs_semicolon {
ctx.expect(Token::Semicolon)?;
}
while ctx.next_if(Token::Semicolon) {}
};
Ok(Self {
items,
final_semicolon,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
if let Some((last, rest)) = self.items.split_last() {
for i in rest {
writeln!(
f,
"{}{}{}",
" ".repeat(ctx.indent),
i.dsp(ctx),
if i.needs_semicolon() { ";" } else { "" }
)?;
}
writeln!(
f,
"{}{}{}",
" ".repeat(ctx.indent),
last.dsp(ctx),
if self.final_semicolon { ";" } else { "" }
)?;
}
Ok(())
}
}
-186
View File
@@ -1,186 +0,0 @@
use crate::parser::VecDspT;
pub use super::*;
pub struct Expr {
span: Span,
ty: ExprTy,
}
pub enum ExprTy {
Block(Body),
Group(Box<Expr>),
Ident(Ident),
Lit(Lit),
Negate(Box<Expr>),
Call { target: Box<Expr>, args: Vec<Expr> },
Assign { target: Box<Expr>, val: Box<Expr> },
If { cond: Box<Expr>, body: Box<Expr> },
Loop { body: Box<Expr> },
While { cond: Box<Expr>, body: Box<Expr> },
Fn(Box<Func>),
}
impl Node for Expr {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut res = Self::unit(ctx)?;
while let Some(next) = ctx.peek() {
let ty = match next {
Token::Equal => {
ctx.next();
let target = Box::new(res);
let val = Box::new(ctx.parse_with(Self::unit)?);
ExprTy::Assign { target, val }
}
Token::OpenParen => {
ctx.next();
let target = Box::new(res);
let args = ctx.list(Token::Comma, Token::CloseParen)?;
ExprTy::Call { target, args }
}
_ => break,
};
res = Self {
ty,
span: ctx.span(),
};
}
Ok(res)
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
self.ty.fmt(f, ctx)
}
}
impl ExprTy {
fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result {
match self {
Self::Ident(ident) => ident.fmt(f, ctx),
Self::Group(expr) => write!(f, "({})", expr.dsp(ctx)),
Self::Fn(func) => func.fmt(f, ctx),
Self::Lit(lit) => write!(f, "{}", lit),
Self::Negate(expr) => {
write!(f, "-{}", expr.dsp(ctx))
}
Self::Call { target, args } => {
write!(f, "{}({})", target.dsp(ctx), args.dsp(ctx))
}
Self::Assign { target, val } => {
write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx))
}
Self::If { cond, body } => {
write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx))
}
Self::While { cond, body } => {
write!(f, "while {} {}", cond.dsp(ctx), body.dsp(ctx))
}
Self::Loop { body } => {
write!(f, "loop {}", body.dsp(ctx))
}
Self::Block(body) => {
write!(f, "{{")?;
if !body.items.is_empty() {
writeln!(f)?;
ctx.indent += 3;
body.fmt(f, ctx)?;
}
write!(f, "}}")?;
Ok(())
}
}
}
}
impl Expr {
pub fn fmt_body(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match self.ty {
ExprTy::Block(_) => self.fmt(f, ctx),
_ => write!(f, "=> {}", self.dsp(ctx)),
}
}
fn unit(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let ty = match ctx.expect_next()? {
Token::Dash => ExprTy::Negate(ctx.parse_box()?),
Token::Ident(s) => ExprTy::Ident(ctx.ident(s)),
Token::Lit(l) => ExprTy::Lit(ctx.lit(l)),
Token::Fn => ExprTy::Fn(ctx.parse_box()?),
Token::If => {
let cond = ctx.parse_box()?;
let body = Box::new(Self::body(ctx)?);
ExprTy::If { cond, body }
}
Token::While => {
let cond = ctx.parse_box()?;
let body = Box::new(Self::body(ctx)?);
ExprTy::While { cond, body }
}
Token::Loop => {
let body = ctx.parse_box()?;
ExprTy::Loop { body }
}
Token::OpenParen => {
if ctx.next_if(Token::CloseParen) {
ExprTy::Lit(Lit {
ty: LitTy::Unit,
span: ctx.span(),
})
} else {
let inner = ctx.parse_box()?;
ctx.expect(Token::CloseParen)?;
ExprTy::Group(inner)
}
}
Token::OpenCurly => {
let body = ctx.parse()?;
ctx.expect(Token::CloseCurly)?;
ExprTy::Block(body)
}
other => return ctx.unexpected(&other, "an expression"),
};
Ok(Self {
ty,
span: ctx.span(),
})
}
pub fn is_group(&self) -> bool {
matches!(self.ty, ExprTy::Group(_))
}
pub fn is_block(&self) -> bool {
matches!(self.ty, ExprTy::Block(_))
}
pub fn block(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> {
ctx.expect(Token::OpenCurly)?;
let id = ctx.parse()?;
ctx.expect(Token::CloseCurly)?;
Ok(Expr {
ty: ExprTy::Block(id),
span: ctx.span(),
})
}
pub fn body(ctx: &mut ParseCtx) -> Result<Expr, CompilerMsg> {
if ctx.next_if(Token::DoubleArrow) {
ctx.parse()
} else {
ctx.parse_with(Expr::block)
}
}
pub fn ends_with_block(&self) -> bool {
match &self.ty {
ExprTy::Block(..) => true,
ExprTy::Loop { body }
| ExprTy::While { body, .. }
| ExprTy::If { body, .. }
| ExprTy::Negate(body)
| ExprTy::Assign { val: body, .. } => body.ends_with_block(),
ExprTy::Fn(f) => f.ends_with_block(),
_ => false,
}
}
}
-62
View File
@@ -1,62 +0,0 @@
use super::*;
pub struct Func {
args: Vec<Param>,
name: Option<Ident>,
ret: Option<Type>,
body: Expr,
span: Span,
}
impl Node for Func {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let mut name = None;
if let Token::Ident(ident) = ctx.expect_peek()? {
// yucky
let ident = ident.to_string();
ctx.next();
let ident = ctx.ident(ident);
name = Some(ident);
}
ctx.expect(Token::OpenParen)?;
let args = ctx.list(Token::Comma, Token::CloseParen)?;
let mut ret = None;
if ctx.next_if(Token::Arrow) {
ret = Some(ctx.parse()?);
}
let body = Expr::body(ctx)?;
Ok(Self {
args,
ret,
body,
name,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
write!(f, "fn")?;
if let Some(name) = &self.name {
write!(f, " {name}")?;
}
write!(f, "(")?;
if let Some((last, rest)) = self.args.split_last() {
for arg in rest {
write!(f, "{}, ", arg.dsp(ctx))?;
}
write!(f, "{}", last.dsp(ctx))?;
}
write!(f, ") ")?;
if let Some(ret) = &self.ret {
write!(f, "-> {} ", ret.dsp(ctx))?;
}
self.body.fmt_body(f, ctx)?;
Ok(())
}
}
impl Func {
pub fn ends_with_block(&self) -> bool {
self.body.ends_with_block()
}
}
-25
View File
@@ -1,25 +0,0 @@
use super::*;
pub struct Ident {
pub name: String,
pub span: Span,
}
impl Node for Ident {
fn parse(ctx: &mut super::ParseCtx) -> Result<Self, crate::io::CompilerMsg> {
match ctx.expect_next()? {
Token::Ident(ident) => Ok(ctx.ident(ident)),
t => ctx.unexpected(&t, "an identifier"),
}
}
fn fmt(&self, f: &mut std::fmt::Formatter, _: DisplayCtx) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl std::fmt::Display for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name.fmt(f)
}
}
-78
View File
@@ -1,78 +0,0 @@
use super::*;
pub struct Item {
pub ty: ItemTy,
pub span: Span,
}
pub enum ItemTy {
Let {
name: Ident,
ty: Option<Type>,
val: Expr,
},
Fn(Func),
Expr(Expr),
Import(Ident),
}
impl Node for Item {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let ty = match ctx.expect_peek()? {
Token::Fn => {
ctx.next();
ItemTy::Fn(ctx.parse()?)
}
Token::Let => {
ctx.next();
let name = ctx.parse()?;
let mut ty = None;
if ctx.next_if(Token::Colon) {
ty = Some(ctx.parse()?);
}
ctx.expect(Token::Equal)?;
let val = ctx.parse()?;
ItemTy::Let { name, ty, val }
}
Token::Import => {
ctx.next();
ItemTy::Import(ctx.parse()?)
}
_ => ItemTy::Expr(ctx.parse()?),
};
Ok(Self {
ty,
span: ctx.span(),
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match &self.ty {
ItemTy::Fn(func) => func.fmt(f, ctx)?,
ItemTy::Let { name, ty, val } => {
write!(f, "let {}", name.dsp(ctx))?;
if let Some(ty) = ty {
write!(f, ": {}", ty.dsp(ctx))?;
}
write!(f, " = {}", val.dsp(ctx))?;
}
ItemTy::Expr(expr) => expr.fmt(f, ctx)?,
ItemTy::Import(ident) => write!(f, "import {}", ident.dsp(ctx))?,
}
Ok(())
}
}
impl Item {
pub fn ends_with_block(&self) -> bool {
match &self.ty {
ItemTy::Let { val, .. } => val.ends_with_block(),
ItemTy::Expr(id) => id.ends_with_block(),
ItemTy::Fn(f) => f.ends_with_block(),
ItemTy::Import(ident) => false,
}
}
pub fn needs_semicolon(&self) -> bool {
!self.ends_with_block()
}
}
-18
View File
@@ -1,18 +0,0 @@
mod body;
mod expr;
mod func;
mod ident;
mod item;
mod param;
mod struct_;
mod ty;
pub use body::*;
pub use expr::*;
pub use func::*;
pub use ident::*;
pub use item::*;
pub use param::*;
pub use ty::*;
use super::{DisplayCtx, Lit, LitTy, Node, ParseCtx, Token};
use crate::io::{CompilerMsg, Span};
-25
View File
@@ -1,25 +0,0 @@
use super::*;
pub struct Param {
name: Ident,
ty: Option<Type>,
}
impl Node for Param {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let name = ctx.parse()?;
let mut ty = None;
if ctx.next_if(Token::Colon) {
ty = Some(ctx.parse()?);
}
Ok(Self { name, ty })
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
self.name.fmt(f, ctx)?;
if let Some(ty) = &self.ty {
write!(f, ": {}", ty.dsp(ctx))?;
}
Ok(())
}
}
-8
View File
@@ -1,8 +0,0 @@
use super::*;
pub struct Struct {
name: String,
fields: Vec<Field>,
}
pub struct Field {}
-20
View File
@@ -1,20 +0,0 @@
use super::*;
pub enum Type {
Ident(Ident),
}
impl Node for Type {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
Ok(match ctx.expect_next()? {
Token::Ident(s) => Self::Ident(ctx.ident(s)),
t => ctx.unexpected(&t, "a type")?,
})
}
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match self {
Type::Ident(id) => id.fmt(f, ctx),
}
}
}
+71
View File
@@ -0,0 +1,71 @@
use std::{iter::Peekable, str::Chars};
#[derive(Clone)]
pub struct CharIter<'a> {
iter: Peekable<Chars<'a>>,
pos: CharPos,
next_pos: CharPos,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CharPos {
line: usize,
col: usize,
}
impl<'a> CharIter<'a> {
pub fn new(text: &'a str) -> Self {
Self {
iter: text.chars().peekable(),
pos: CharPos::default(),
next_pos: CharPos::default(),
}
}
pub fn next(&mut self) -> Option<char> {
let next = self.iter.next();
self.advance(next)
}
fn advance(&mut self, c: Option<char>) -> Option<char> {
self.pos = self.next_pos;
if let Some(c) = c {
if c == '\n' {
self.next_pos.line += 1;
self.next_pos.col = 0;
} else {
self.next_pos.col += 1;
}
}
c
}
pub fn peek(&mut self) -> Option<char> {
self.iter.peek().copied()
}
pub fn next_if(&mut self, f: impl FnOnce(&char) -> bool) -> Option<char> {
let next = self.iter.next_if(f);
self.advance(next)
}
pub fn pos(&self) -> CharPos {
self.pos
}
pub fn until(&mut self, until: char) -> Option<String> {
let mut str = String::new();
let mut next = self.next()?;
while next != until {
str.push(next);
next = self.next()?;
}
Some(str)
}
}
impl<'a> From<&'a str> for CharIter<'a> {
fn from(value: &'a str) -> Self {
Self::new(value)
}
}
+24
View File
@@ -0,0 +1,24 @@
use super::*;
#[derive(Debug, Clone, PartialEq)]
pub enum Keyword {
Let,
Fn,
}
impl Keyword {
pub fn parse(ident: &str) -> Option<Self> {
Some(match ident {
"let" => Self::Let,
"fn" => Self::Fn,
_ => return None,
})
}
}
impl From<Keyword> for Token {
fn from(value: Keyword) -> Self {
Token::Keyword(value)
}
}
+4
View File
@@ -0,0 +1,4 @@
#[derive(Debug, Clone, PartialEq)]
pub enum Literal {
String(String),
}
+134
View File
@@ -0,0 +1,134 @@
use super::io::*;
mod chr;
mod kw;
mod lit;
mod symbol;
pub use chr::*;
pub use kw::*;
pub use lit::*;
pub use symbol::*;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
Lit(Literal),
Keyword(Keyword),
Ident(String),
Symbol(Symbol),
}
#[derive(Clone)]
pub struct TokenCursor<'a> {
iter: CharIter<'a>,
prev_sym: Option<Symbol>,
next: Option<(Token, CharSpan)>,
next_start: CharPos,
prev_end: CharPos,
}
impl<'a> TokenCursor<'a> {
pub fn new(iter: impl Into<CharIter<'a>>) -> Self {
let mut s = Self {
next: None,
prev_sym: None,
iter: iter.into(),
next_start: CharPos::default(),
prev_end: CharPos::default(),
};
s.next();
s
}
pub fn next(&mut self) -> Option<Token> {
self.next_span().map(|n| n.0)
}
pub fn next_span(&mut self) -> Option<(Token, CharSpan)> {
self.prev_end = self.iter.pos();
self.prev_sym = self.next.as_ref().and_then(|n| match n.0 {
Token::Symbol(s) => Some(s),
_ => None,
});
while self.iter.next_if(|c| c.is_whitespace()).is_some() {}
self.next_start = self.iter.pos();
std::mem::replace(&mut self.next, Self::get_next(&mut self.iter))
}
fn get_next(iter: &mut CharIter) -> Option<(Token, CharSpan)> {
while iter.next_if(|c| c.is_whitespace()).is_some() {}
if let Some(c) = iter.next() {
let start = iter.pos();
let val = Self::get_next_inner(iter, c);
let span = start.to(iter.pos());
val.map(|v| (v, span))
} else {
None
}
}
fn get_next_inner(iter: &mut CharIter, c: char) -> Option<Token> {
if c == '"' {
return iter.until('"').map(|s| Token::Lit(Literal::String(s)));
}
if let Some(sym) = Symbol::parse(c, iter) {
return Some(Token::Symbol(sym));
}
let mut ident = c.to_string();
while let Some(c) = iter.next_if(|c| !c.is_whitespace() && Symbol::parse_char(*c).is_none())
{
ident.push(c);
}
Some(if let Some(kw) = Keyword::parse(&ident) {
Token::Keyword(kw)
} else {
Token::Ident(ident)
})
}
pub fn peek(&self) -> Option<&Token> {
self.peek_span().map(|v| v.0)
}
pub fn peek_span(&self) -> Option<(&Token, CharSpan)> {
self.next.as_ref().map(|(t, s)| (t, *s))
}
pub fn next_if(&mut self, f: impl FnOnce(&Token) -> bool) -> Option<Token> {
if self.peek().is_some_and(f) {
self.next()
} else {
None
}
}
pub fn next_is(&mut self, token: impl Into<Token>) -> bool {
self.next_is_ref(&token.into())
}
pub fn peek_is(&mut self, token: impl Into<Token>) -> bool {
self.peek().is_some_and(|t| *t == token.into())
}
pub fn next_is_ref(&mut self, token: &Token) -> bool {
self.next_if(|t| t == token).is_some()
}
pub fn next_start(&self) -> CharPos {
self.next_start
}
pub fn prev_end(&self) -> CharPos {
self.prev_end
}
pub fn prev_sym(&self) -> Option<Symbol> {
self.prev_sym
}
}
impl<'a, T: Into<CharIter<'a>>> From<T> for TokenCursor<'a> {
fn from(value: T) -> Self {
Self::new(value.into())
}
}
+83
View File
@@ -0,0 +1,83 @@
use super::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Symbol {
// 1 char
OpenParen,
CloseParen,
OpenCurly,
CloseCurly,
Plus,
Minus,
Slash,
Asterisk,
Equal,
Colon,
Semicolon,
SingleQuote,
Comma,
// 2 chars
Arrow,
DoubleArrow,
}
impl Symbol {
pub fn parse(c: char, iter: &mut CharIter) -> Option<Self> {
Self::parse_char(c).map(|s| s.parse_rest(iter))
}
pub fn parse_char(c: char) -> Option<Self> {
Some(match c {
'(' => Symbol::OpenParen,
')' => Symbol::CloseParen,
'{' => Symbol::OpenCurly,
'}' => Symbol::CloseCurly,
'+' => Symbol::Plus,
'-' => Symbol::Minus,
'/' => Symbol::Slash,
'*' => Symbol::Asterisk,
'=' => Symbol::Equal,
':' => Symbol::Colon,
';' => Symbol::Semicolon,
'\'' => Symbol::SingleQuote,
',' => Symbol::Comma,
_ => return None,
})
}
pub fn parse_rest(mut self, iter: &mut CharIter) -> Self {
let Some(next) = iter.peek() else {
return self;
};
match (self, next) {
(Symbol::Minus, '>') => self = Symbol::Arrow,
(Symbol::Equal, '>') => self = Symbol::DoubleArrow,
_ => return self,
}
iter.next();
self
}
pub fn str(&self) -> &'static str {
match self {
Symbol::OpenParen => "(",
Symbol::CloseParen => ")",
Symbol::OpenCurly => "{",
Symbol::CloseCurly => "}",
Symbol::Plus => "+",
Symbol::Minus => "-",
Symbol::Slash => "/",
Symbol::Asterisk => "*",
Symbol::Equal => "=",
Symbol::Colon => ":",
Symbol::Semicolon => ";",
Symbol::SingleQuote => "'",
Symbol::Arrow => "->",
Symbol::DoubleArrow => "=>",
Symbol::Comma => ",",
}
}
}
impl From<Symbol> for Token {
fn from(value: Symbol) -> Self {
Token::Symbol(value)
}
}
+30
View File
@@ -0,0 +1,30 @@
use super::*;
#[derive(Debug)]
pub struct PBlock {
statements: Vec<Node<PStatement>>,
return_last: bool,
}
impl Parsable for PBlock {
type Data = bool;
fn parse(ctx: &mut ParserCtx, curlies: bool) -> ParseResult<Self> {
let end = if curlies {
ctx.expect(Symbol::OpenCurly)?;
Some(Symbol::CloseCurly)
} else {
None
};
let res = ctx.parse_list(
end,
SepCheck::new(Symbol::Semicolon)
.dup(true)
.skip_if(|ctx, _| ctx.prev_sym().is_some_and(|s| s == Symbol::CloseCurly)),
)?;
ParseResult::Ok(Self {
statements: res.nodes,
return_last: res.last_sep,
})
}
}
+52
View File
@@ -0,0 +1,52 @@
use super::*;
#[derive(Debug)]
pub enum PExpr {
Ident(String),
Lit(Literal),
Block(Node<PBlock>),
Group(BNode<PExpr>),
Unit,
}
impl Parsable for PExpr {
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
Self::parse_unit(ctx)
}
}
impl PExpr {
fn parse_unit(ctx: &mut ParserCtx) -> ParseResult<Self> {
ParseResult::Ok(match ctx.expect_peek()? {
Token::Lit(lit) => {
let res = PExpr::Lit(lit.clone());
ctx.next();
res
}
Token::Ident(ident) => {
let res = PExpr::Ident(ident.to_string());
ctx.next();
res
}
Token::Symbol(symbol) => match symbol {
Symbol::OpenParen => {
ctx.expect_next()?;
if ctx.next_is(Symbol::CloseParen) {
PExpr::Unit
} else {
let inner = ctx.parse();
let Some(inner) = inner else {
ctx.seek(Symbol::CloseParen);
return ParseResult::SubErr;
};
ctx.expect(Symbol::CloseParen)?;
PExpr::Group(inner.bx())
}
}
Symbol::OpenCurly => PExpr::Block(ctx.parse_with(true)?),
_ => return ctx.unexpected("expression").res(),
},
_ => return ctx.unexpected("expression").res(),
})
}
}
+25
View File
@@ -0,0 +1,25 @@
use super::*;
#[derive(Debug)]
pub struct PFunc {
name: Node<PIdent>,
args: Vec<Node<PVarDef>>,
body: Node<PExpr>,
}
impl Parsable for PFunc {
type Data = ();
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
ctx.expect(Keyword::Fn)?;
let name = ctx.parse()?;
ctx.expect(Symbol::OpenParen)?;
let args = ctx.parse_list(Some(Symbol::CloseParen), SepCheck::new(Symbol::Comma))?;
let body = ctx.parse()?;
ParseResult::Ok(Self {
name,
args: args.nodes,
body,
})
}
}
+25
View File
@@ -0,0 +1,25 @@
use super::*;
use std::ops::Deref;
#[derive(Debug)]
pub struct PIdent(String);
impl Deref for PIdent {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl Parsable for PIdent {
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
if let Token::Ident(ident) = ctx.expect_peek()? {
let ident = ident.clone();
ctx.next();
ParseResult::Ok(Self(ident))
} else {
ctx.unexpected("identifier").res()
}
}
}
+128
View File
@@ -0,0 +1,128 @@
use super::*;
pub struct ListRes<T> {
pub nodes: Vec<Node<T>>,
pub last_sep: bool,
}
pub trait BetweenFn<T> {
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool;
}
impl ParserCtx<'_> {
pub fn parse_list<T: Parsable<Data = ()>>(
&mut self,
end: Option<impl Into<Token>>,
mut between: impl BetweenFn<T>,
) -> Option<ListRes<T>> {
let end = end.map(|t| t.into());
let mut nodes = Vec::new();
let mut last_sep = false;
macro_rules! abort {
() => {
if end.is_some_and(|t| self.seek(t)) {
break;
} else {
return None;
}
};
}
macro_rules! check_end {
() => {
if end.as_ref().is_some_and(|t| self.next_is_ref(t))
|| (end.is_none() && self.peek().is_none())
{
break;
}
};
}
loop {
check_end!();
last_sep = false;
nodes.push(match self.parse() {
Some(node) => node,
None => abort!(),
});
check_end!();
if between.run(self, nodes.last().unwrap()) {
abort!();
}
last_sep = true;
}
Some(ListRes { nodes, last_sep })
}
}
pub struct SepCheck {
pub sep: Token,
pub dup: bool,
}
impl<T> BetweenFn<T> for SepCheck {
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool {
let Some(next) = ctx.expect_next() else {
return true;
};
if next != self.sep {
ctx.msgs
.push(ctx.unexpected(format!("Expected {:?}", self.sep)));
return true;
}
if self.dup {
while ctx.next_is_ref(&self.sep) {}
}
false
}
}
impl SepCheck {
pub fn new(sep: impl Into<Token>) -> Self {
Self {
sep: sep.into(),
dup: false,
}
}
pub fn dup(mut self, dup: bool) -> Self {
self.dup = dup;
self
}
}
pub trait SkipFn<T> = Fn(&mut ParserCtx, &Node<T>) -> bool;
// I hate everything. sepcheck is fine, this is not
pub struct SkipIf<T, F: SkipFn<T>, B: BetweenFn<T>> {
f: F,
inner: B,
_pd: std::marker::PhantomData<T>,
}
impl<T, F: SkipFn<T>, B: BetweenFn<T>> SkipIf<T, F, B> {
pub fn new(f: F, run: B) -> Self {
Self {
f,
inner: run,
_pd: std::marker::PhantomData,
}
}
}
impl<T, F: SkipFn<T>, B: BetweenFn<T>> BetweenFn<T> for SkipIf<T, F, B> {
fn run(&mut self, ctx: &mut ParserCtx, prev: &Node<T>) -> bool {
if (self.f)(ctx, prev) {
false
} else {
self.inner.run(ctx, prev)
}
}
}
pub trait BetweenFnUtil<T>: BetweenFn<T> + Sized {
fn skip_if<F: SkipFn<T>>(self, f: F) -> SkipIf<T, F, Self>;
}
impl<B: BetweenFn<T>, T> BetweenFnUtil<T> for B {
fn skip_if<F: SkipFn<T>>(self, f: F) -> SkipIf<T, F, Self> {
SkipIf::new(f, self)
}
}
+21
View File
@@ -0,0 +1,21 @@
mod block;
mod expr;
mod func;
mod ident;
mod list;
mod node;
mod statement;
mod ty;
mod vardef;
pub use block::*;
pub use expr::*;
pub use func::*;
pub use ident::*;
pub use list::*;
pub use node::*;
pub use statement::*;
pub use ty::*;
pub use vardef::*;
use super::*;
+63
View File
@@ -0,0 +1,63 @@
use super::*;
pub struct Node<T> {
pub data: Option<T>,
}
pub type BNode<T> = Box<Node<T>>;
pub enum ParseResult<T> {
Ok(T),
Node(Node<T>),
Continue(CompilerMsg),
Break(CompilerMsg),
SubErr,
}
pub trait Parsable: Sized {
type Data = ();
fn parse(ctx: &mut ParserCtx, data: Self::Data) -> ParseResult<Self>;
}
impl<T> Node<T> {
pub fn bx(self) -> Box<Self> {
Box::new(self)
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Node<U> {
Node {
data: self.data.map(f),
}
}
}
use std::convert::Infallible;
impl<T> std::ops::FromResidual<Option<Infallible>> for ParseResult<T> {
fn from_residual(residual: Option<Infallible>) -> Self {
match residual {
None => ParseResult::SubErr,
}
}
}
impl<T> std::ops::FromResidual<Result<Infallible, CompilerMsg>> for ParseResult<T> {
fn from_residual(residual: Result<Infallible, CompilerMsg>) -> Self {
match residual {
Err(msg) => ParseResult::Break(msg),
}
}
}
impl CompilerMsg {
pub fn res<T>(self) -> ParseResult<T> {
ParseResult::Break(self)
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for Node<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(d) = &self.data {
d.fmt(f)
} else {
f.write_str("{error}")
}
}
}
+27
View File
@@ -0,0 +1,27 @@
use super::*;
#[derive(Debug)]
pub enum PStatement {
Expr(PExpr),
Let(Node<PVarDef>, Node<PExpr>),
Fn(PFunc),
}
impl Parsable for PStatement {
fn parse(ctx: &mut ParserCtx, _: ()) -> ParseResult<Self> {
let res = match ctx.expect_peek()? {
Token::Keyword(kw) => match kw {
Keyword::Let => {
ctx.next();
let name = ctx.parse()?;
ctx.expect(Symbol::Equal)?;
let body = ctx.parse()?;
Self::Let(name, body)
}
Keyword::Fn => return ParseResult::Node(ctx.parse()?.map(PStatement::Fn)),
},
_ => return ParseResult::Node(ctx.parse()?.map(PStatement::Expr)),
};
ParseResult::Ok(res)
}
}
+12
View File
@@ -0,0 +1,12 @@
use super::*;
#[derive(Debug)]
pub struct PType {
name: Node<PIdent>,
}
impl Parsable for PType {
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
ParseResult::Ok(Self { name: ctx.parse()? })
}
}
+18
View File
@@ -0,0 +1,18 @@
use super::*;
#[derive(Debug)]
pub struct PVarDef {
name: Node<PIdent>,
ty: Option<Node<PType>>,
}
impl Parsable for PVarDef {
fn parse(ctx: &mut ParserCtx, _: Self::Data) -> ParseResult<Self> {
let name = ctx.parse()?;
let mut ty = None;
if ctx.next_is(Symbol::Colon) {
ty = Some(ctx.parse()?);
}
ParseResult::Ok(Self { name, ty })
}
}
-23
View File
@@ -1,23 +0,0 @@
use crate::{
io::CompilerOutput,
ir::{Ir, Namespace},
parser::{self, parse_file},
};
pub fn parse_program(path: &str, output: &mut CompilerOutput) -> Option<Ir> {
let root = parse_file(path, output)?;
let mut ir = Ir::default();
add_defs(ir.root(), &root);
Some(ir)
}
pub fn add_defs(namespace: &mut Namespace, body: &parser::Body) {
for item in &body.items {
match &item.ty {
parser::ItemTy::Let { name, ty, val } => todo!(),
parser::ItemTy::Fn(func) => todo!(),
parser::ItemTy::Expr(expr) => todo!(),
parser::ItemTy::Import(ident) => todo!(),
}
}
}
+8
View File
@@ -0,0 +1,8 @@
let x = "test";
let y = "test";
fn test(x: u32) "hello";
fn test3() {
arst
}
fn test2() "hello";
-14
View File
@@ -1,14 +0,0 @@
modl other;
let x: i32 = 3;
while true {
print("hello");
print(x);
};
let y = true;
if y => print("hello");
fn thing() {
}
-3
View File
@@ -1,3 +0,0 @@
fn thing() {
print("hello from other");
}