Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 867f9e51bd |
Generated
+1
-1
@@ -3,5 +3,5 @@
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "lang"
|
||||
name = "v2"
|
||||
version = "0.1.0"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "lang"
|
||||
name = "v2"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
-121
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
mod namespace;
|
||||
pub use namespace::*;
|
||||
|
||||
use super::Id;
|
||||
@@ -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
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-26
@@ -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;
|
||||
}
|
||||
};
|
||||
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)
|
||||
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:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub struct Struct {
|
||||
name: String,
|
||||
fields: Vec<Field>,
|
||||
}
|
||||
|
||||
pub struct Field {}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()? })
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
let x = "test";
|
||||
let y = "test";
|
||||
|
||||
fn test(x: u32) "hello";
|
||||
fn test3() {
|
||||
arst
|
||||
}
|
||||
fn test2() "hello";
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fn thing() {
|
||||
print("hello from other");
|
||||
}
|
||||
Reference in New Issue
Block a user