Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 867f9e51bd |
Generated
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lang"
|
name = "v2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lang"
|
name = "v2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
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::{
|
#![feature(try_trait_v2)]
|
||||||
io::CompilerOutput,
|
#![feature(associated_type_defaults)]
|
||||||
parser::{Node, parse_file},
|
#![feature(trait_alias)]
|
||||||
parser_ir::parse_program,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod io;
|
|
||||||
mod ir;
|
|
||||||
mod parser;
|
mod parser;
|
||||||
mod parser_ir;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut args = std::env::args();
|
parser::parse(include_str!("test.lang"));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ctx;
|
||||||
mod node;
|
mod token;
|
||||||
mod nodes;
|
mod tree;
|
||||||
|
mod io;
|
||||||
|
|
||||||
use cursor::*;
|
pub use ctx::*;
|
||||||
pub use node::*;
|
use token::*;
|
||||||
pub use nodes::*;
|
pub use tree::*;
|
||||||
|
pub use io::*;
|
||||||
|
|
||||||
use crate::io::CompilerOutput;
|
pub fn parse(file: &str) {
|
||||||
|
let mut msgs = Vec::new();
|
||||||
pub fn parse_file(path: &str, output: &mut CompilerOutput) -> Option<Body> {
|
let mut parser = ParserCtx::new(file, &mut msgs);
|
||||||
let code = match std::fs::read_to_string(path) {
|
if let Some(block) = parser.parse_with::<PBlock>(false) {
|
||||||
Ok(code) => code,
|
println!("{block:#?}");
|
||||||
Err(err) => {
|
} else {
|
||||||
output.error(format!("Failed to read input file: {err}"));
|
println!("{msgs:?}");
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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