This commit is contained in:
2026-04-18 00:16:03 -04:00
parent b3f77076d4
commit d864adfd05
12 changed files with 167 additions and 38 deletions
+54
View File
@@ -0,0 +1,54 @@
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> {}
+20 -1
View File
@@ -1,4 +1,23 @@
mod id;
mod structs; mod structs;
pub use id::*;
pub use structs::*; pub use structs::*;
pub struct Ir {} pub struct Ir {
pub root: Id<Namespace>,
pub namespaces: IdVec<Namespace>,
}
impl Ir {
pub fn root(&mut self) -> &mut Namespace {
&mut self.namespaces[self.root]
}
}
impl Default for Ir {
fn default() -> Self {
let mut namespaces = IdVec::default();
let root = namespaces.add(Namespace::default());
Self { root, namespaces }
}
}
+4 -1
View File
@@ -1 +1,4 @@
pub struct Module {} mod namespace;
pub use namespace::*;
use super::Id;
+17
View File
@@ -0,0 +1,17 @@
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 -8
View File
@@ -1,11 +1,13 @@
use crate::{ use crate::{
io::CompilerOutput, io::CompilerOutput,
parser::{Node, parse_root}, parser::{Node, parse_file},
parser_ir::parse_program,
}; };
mod io; mod io;
mod ir; mod ir;
mod parser; mod parser;
mod parser_ir;
fn main() { fn main() {
let mut args = std::env::args(); let mut args = std::env::args();
@@ -14,15 +16,9 @@ fn main() {
return; return;
}; };
let mut output = CompilerOutput::new(); let mut output = CompilerOutput::new();
let root = parse_root(&path, &mut output); let root = parse_file(&path, &mut output);
if let Some(root) = root { if let Some(root) = root {
print!("{}", root.new_dsp()); print!("{}", root.new_dsp());
// for item in &root.items {
// output.errors.push(io::CompilerMsg {
// spans: vec![item.span],
// msg: format!("hello"),
// });
// }
} }
output.write(&mut std::io::stdout()); output.write(&mut std::io::stdout());
} }
+1 -1
View File
@@ -31,7 +31,7 @@ def_tokens! {
} }
keyword { keyword {
Let: "let", Let: "let",
Do: "do", Import: "import",
Fn: "fn", Fn: "fn",
If: "if", If: "if",
Loop: "loop", Loop: "loop",
+22
View File
@@ -5,3 +5,25 @@ mod nodes;
use cursor::*; use cursor::*;
pub use node::*; pub use node::*;
pub use nodes::*; pub use nodes::*;
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)
}
+2 -25
View File
@@ -1,13 +1,10 @@
use crate::{
io::{CompilerMsg, CompilerOutput},
parser::{Cursor, nodes::*},
};
mod ctx; mod ctx;
mod dsp; mod dsp;
pub use ctx::*; pub use ctx::*;
pub use dsp::*; pub use dsp::*;
use crate::io::CompilerMsg;
pub trait Node: Sized { pub trait Node: Sized {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg>; fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg>;
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result; fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result;
@@ -18,23 +15,3 @@ pub trait Node: Sized {
self.dsp(DisplayCtx { indent: 0 }) self.dsp(DisplayCtx { indent: 0 })
} }
} }
pub fn parse_root(path: &str, output: &mut CompilerOutput) -> Option<Body> {
let root_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(&root_code, 0));
let root = match ctx.parse() {
Ok(v) => v,
Err(msg) => {
output.error(msg);
return None;
}
};
Some(root)
}
+15 -1
View File
@@ -11,12 +11,18 @@ pub enum ItemTy {
ty: Option<Type>, ty: Option<Type>,
val: Expr, val: Expr,
}, },
Fn(Func),
Expr(Expr), Expr(Expr),
Import(Ident),
} }
impl Node for Item { impl Node for Item {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> { fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
let ty = match ctx.expect_peek()? { let ty = match ctx.expect_peek()? {
Token::Fn => {
ctx.next();
ItemTy::Fn(ctx.parse()?)
}
Token::Let => { Token::Let => {
ctx.next(); ctx.next();
let name = ctx.parse()?; let name = ctx.parse()?;
@@ -28,6 +34,10 @@ impl Node for Item {
let val = ctx.parse()?; let val = ctx.parse()?;
ItemTy::Let { name, ty, val } ItemTy::Let { name, ty, val }
} }
Token::Import => {
ctx.next();
ItemTy::Import(ctx.parse()?)
}
_ => ItemTy::Expr(ctx.parse()?), _ => ItemTy::Expr(ctx.parse()?),
}; };
Ok(Self { Ok(Self {
@@ -38,6 +48,7 @@ impl Node for Item {
fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter, ctx: DisplayCtx) -> std::fmt::Result {
match &self.ty { match &self.ty {
ItemTy::Fn(func) => func.fmt(f, ctx)?,
ItemTy::Let { name, ty, val } => { ItemTy::Let { name, ty, val } => {
write!(f, "let {}", name.dsp(ctx))?; write!(f, "let {}", name.dsp(ctx))?;
if let Some(ty) = ty { if let Some(ty) = ty {
@@ -45,7 +56,8 @@ impl Node for Item {
} }
write!(f, " = {}", val.dsp(ctx))?; write!(f, " = {}", val.dsp(ctx))?;
} }
ItemTy::Expr(id) => id.fmt(f, ctx)?, ItemTy::Expr(expr) => expr.fmt(f, ctx)?,
ItemTy::Import(ident) => write!(f, "import {}", ident.dsp(ctx))?,
} }
Ok(()) Ok(())
} }
@@ -56,6 +68,8 @@ impl Item {
match &self.ty { match &self.ty {
ItemTy::Let { val, .. } => val.ends_with_block(), ItemTy::Let { val, .. } => val.ends_with_block(),
ItemTy::Expr(id) => id.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 { pub fn needs_semicolon(&self) -> bool {
+23
View File
@@ -0,0 +1,23 @@
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!(),
}
}
}
+2 -1
View File
@@ -1,3 +1,5 @@
modl other;
let x: i32 = 3; let x: i32 = 3;
while true { while true {
print("hello"); print("hello");
@@ -9,5 +11,4 @@ let y = true;
if y => print("hello"); if y => print("hello");
fn thing() { fn thing() {
} }
+3
View File
@@ -0,0 +1,3 @@
fn thing() {
print("hello from other");
}