steal from jai

This commit is contained in:
2026-06-01 22:40:24 -04:00
parent d864adfd05
commit 1d568f8ce3
17 changed files with 231 additions and 84 deletions
+14 -3
View File
@@ -1,3 +1,5 @@
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Span { pub struct Span {
pub file: usize, pub file: usize,
@@ -32,7 +34,7 @@ pub struct CompilerMsg {
#[derive(Default)] #[derive(Default)]
pub struct CompilerOutput { pub struct CompilerOutput {
pub errors: Vec<CompilerMsg>, pub errors: Vec<CompilerMsg>,
pub files: Vec<String>, pub files: Vec<PathBuf>,
} }
impl CompilerOutput { impl CompilerOutput {
@@ -112,10 +114,19 @@ impl Span {
} }
impl From<String> for CompilerMsg { impl From<String> for CompilerMsg {
fn from(value: String) -> Self { fn from(msg: String) -> Self {
Self { Self {
spans: Vec::new(), spans: Vec::new(),
msg: value.to_string(), msg,
}
}
}
impl<S: Into<String>> From<(S, Span)> for CompilerMsg {
fn from((msg, span): (S, Span)) -> Self {
Self {
spans: vec![span],
msg: msg.into(),
} }
} }
} }
+3
View File
@@ -2,3 +2,6 @@ mod namespace;
pub use namespace::*; pub use namespace::*;
use super::Id; use super::Id;
pub struct Fn {
}
+1 -7
View File
@@ -1,17 +1,11 @@
use super::*; use super::*;
use crate::parser::Ident;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Default)] #[derive(Default)]
pub struct Namespace { pub struct Namespace {
pub items: HashMap<Ident, Item>, pub items: HashMap<String, Item>,
} }
pub enum Item { pub enum Item {
Import(Id<Namespace>), 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
+2 -9
View File
@@ -1,8 +1,4 @@
use crate::{ use crate::{io::CompilerOutput, parser_ir::parse_program};
io::CompilerOutput,
parser::{Node, parse_file},
parser_ir::parse_program,
};
mod io; mod io;
mod ir; mod ir;
@@ -16,9 +12,6 @@ fn main() {
return; return;
}; };
let mut output = CompilerOutput::new(); let mut output = CompilerOutput::new();
let root = parse_file(&path, &mut output); let ir = parse_program(&path, &mut output);
if let Some(root) = root {
print!("{}", root.new_dsp());
}
output.write(&mut std::io::stdout()); output.write(&mut std::io::stdout());
} }
+3 -3
View File
@@ -64,12 +64,12 @@ impl<'a> Cursor<'a> {
if next == *token { if next == *token {
Ok(next) Ok(next)
} else { } else {
self.unexpected(&next, &format!("'{token}'")) self.unexpected(next, &format!("'{token}'"))
} }
} }
pub fn unexpected<T>(&self, token: &Token, expected: &str) -> Result<T, CompilerMsg> { pub fn unexpected<T>(&self, token: Token, expected: &str) -> Result<T, CompilerMsg> {
Err(CompilerMsg::unexpected_token(token, self.span, expected)) Err(CompilerMsg::unexpected_token(&token, self.span, expected))
} }
pub fn peek_start(&mut self) -> usize { pub fn peek_start(&mut self) -> usize {
+2 -5
View File
@@ -26,7 +26,6 @@ def_tokens! {
DashEqual: "-=", DashEqual: "-=",
AsteriskEqual: "*=", AsteriskEqual: "*=",
SlashEqual: "/=", SlashEqual: "/=",
DoubleColon: "::",
Hash: "#", Hash: "#",
} }
keyword { keyword {
@@ -38,6 +37,7 @@ def_tokens! {
While: "while", While: "while",
For: "for", For: "for",
Match: "match", Match: "match",
Break: "break",
} }
other { other {
Ident(String), Ident(String),
@@ -112,10 +112,7 @@ impl Iterator for Tokens<'_> {
_ => Token::Slash, _ => Token::Slash,
'=' => Token::SlashEqual, '=' => Token::SlashEqual,
}, },
':' => then! { ':' => Token::Colon,
_ => Token::Colon,
':' => Token::DoubleColon,
},
';' => Token::Semicolon, ';' => Token::Semicolon,
'=' => then! { '=' => then! {
_ => Token::Equal, _ => Token::Equal,
+7 -4
View File
@@ -2,22 +2,25 @@ mod cursor;
mod node; mod node;
mod nodes; mod nodes;
use std::path::Path;
use cursor::*; use cursor::*;
pub use node::*; pub use node::*;
pub use nodes::*; pub use nodes::*;
use crate::io::CompilerOutput; use crate::io::CompilerOutput;
pub fn parse_file(path: &str, output: &mut CompilerOutput) -> Option<Body> { pub fn parse_file(path: impl AsRef<Path>, output: &mut CompilerOutput) -> Option<Body> {
let code = match std::fs::read_to_string(path) { let code = match std::fs::read_to_string(&path) {
Ok(code) => code, Ok(code) => code,
Err(err) => { Err(err) => {
output.error(format!("Failed to read input file: {err}")); output.error(format!("Failed to read input file: {err}"));
return None; return None;
} }
}; };
output.files.push(path.to_string()); let file = output.files.len();
let mut ctx = ParseCtx::new(Cursor::new(&code, 0)); output.files.push(path.as_ref().to_path_buf());
let mut ctx = ParseCtx::new(Cursor::new(&code, file));
let root = match ctx.parse() { let root = match ctx.parse() {
Ok(v) => v, Ok(v) => v,
Err(msg) => { Err(msg) => {
+1 -1
View File
@@ -30,7 +30,7 @@ impl<'a> ParseCtx<'a> {
) -> Result<N, CompilerMsg> { ) -> Result<N, CompilerMsg> {
let old_start = self.start; let old_start = self.start;
self.start = self.cursor.peek_start(); self.start = self.cursor.peek_start();
let res = f(self).map(|r| r); let res = f(self);
self.start = old_start; self.start = old_start;
res res
} }
+4 -4
View File
@@ -1,7 +1,7 @@
use super::*; use super::*;
pub struct Body { pub struct Body {
pub items: Vec<Item>, pub items: Vec<Expr>,
pub final_semicolon: bool, pub final_semicolon: bool,
pub span: Span, pub span: Span,
} }
@@ -16,9 +16,9 @@ impl Node for Body {
if at_end(ctx) { if at_end(ctx) {
break true; break true;
} }
let item: Item = ctx.parse()?; let expr: Expr = ctx.parse()?;
let needs_semicolon = item.needs_semicolon(); let needs_semicolon = expr.needs_semicolon();
items.push(item); items.push(expr);
if at_end(ctx) { if at_end(ctx) {
break false; break false;
} }
+92 -8
View File
@@ -3,22 +3,48 @@ use crate::parser::VecDspT;
pub use super::*; pub use super::*;
pub struct Expr { pub struct Expr {
span: Span, pub span: Span,
ty: ExprTy, pub ty: ExprTy,
} }
pub enum ExprTy { pub enum ExprTy {
Block(Body), Block(Body),
Group(Box<Expr>), Group(Box<Expr>),
Member {
of: Box<Expr>,
field: Ident,
},
Ident(Ident), Ident(Ident),
Lit(Lit), Lit(Lit),
Negate(Box<Expr>), Negate(Box<Expr>),
Call { target: Box<Expr>, args: Vec<Expr> }, Call {
Assign { target: Box<Expr>, val: Box<Expr> }, target: Box<Expr>,
If { cond: Box<Expr>, body: Box<Expr> }, args: Vec<Expr>,
Loop { body: Box<Expr> }, },
While { cond: Box<Expr>, body: Box<Expr> }, Assign {
target: Box<Expr>,
val: Box<Expr>,
},
Define {
target: Box<Expr>,
ty: Option<Type>,
const_: bool,
val: Box<Expr>,
},
If {
cond: Box<Expr>,
body: Box<Expr>,
},
Loop {
body: Box<Expr>,
},
While {
cond: Box<Expr>,
body: Box<Expr>,
},
Import(Ident),
Fn(Box<Func>), Fn(Box<Func>),
Break,
} }
impl Node for Expr { impl Node for Expr {
@@ -32,12 +58,39 @@ impl Node for Expr {
let val = Box::new(ctx.parse_with(Self::unit)?); let val = Box::new(ctx.parse_with(Self::unit)?);
ExprTy::Assign { target, val } ExprTy::Assign { target, val }
} }
Token::Colon => {
ctx.next();
let target = Box::new(res);
let mut ty = None;
let next = ctx.expect_peek()?;
if !matches!(next, Token::Equal | Token::Colon) {
ty = Some(ctx.parse()?);
}
let const_ = match ctx.expect_next()? {
Token::Equal => false,
Token::Colon => true,
t => ctx.unexpected(t, "an equals = or colon :")?,
};
let val = Box::new(ctx.parse_with(Self::unit)?);
ExprTy::Define {
target,
ty,
val,
const_,
}
}
Token::OpenParen => { Token::OpenParen => {
ctx.next(); ctx.next();
let target = Box::new(res); let target = Box::new(res);
let args = ctx.list(Token::Comma, Token::CloseParen)?; let args = ctx.list(Token::Comma, Token::CloseParen)?;
ExprTy::Call { target, args } ExprTy::Call { target, args }
} }
Token::Dot => {
ctx.next();
let of = Box::new(res);
let field = ctx.parse()?;
ExprTy::Member { of, field }
}
_ => break, _ => break,
}; };
res = Self { res = Self {
@@ -69,6 +122,21 @@ impl ExprTy {
Self::Assign { target, val } => { Self::Assign { target, val } => {
write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx)) write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx))
} }
Self::Define {
target,
ty,
val,
const_,
} => {
write!(f, "{} :", target.dsp(ctx))?;
if let Some(ty) = ty {
write!(f, " {} ", ty.dsp(ctx))?;
}
write!(f, "{} {}", if *const_ { ":" } else { "=" }, val.dsp(ctx))
}
Self::Member { of, field } => {
write!(f, "{}.{field}", of.dsp(ctx))
}
Self::If { cond, body } => { Self::If { cond, body } => {
write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx)) write!(f, "if {} {}", cond.dsp(ctx), body.dsp(ctx))
} }
@@ -88,6 +156,12 @@ impl ExprTy {
write!(f, "}}")?; write!(f, "}}")?;
Ok(()) Ok(())
} }
Self::Import(ident) => {
write!(f, "import {ident}")
}
Self::Break => {
write!(f, "break")
}
} }
} }
} }
@@ -137,7 +211,12 @@ impl Expr {
ctx.expect(Token::CloseCurly)?; ctx.expect(Token::CloseCurly)?;
ExprTy::Block(body) ExprTy::Block(body)
} }
other => return ctx.unexpected(&other, "an expression"), Token::Break => ExprTy::Break,
Token::Import => {
let ident = ctx.parse()?;
ExprTy::Import(ident)
}
other => return ctx.unexpected(other, "an expression"),
}; };
Ok(Self { Ok(Self {
ty, ty,
@@ -179,8 +258,13 @@ impl Expr {
| ExprTy::If { body, .. } | ExprTy::If { body, .. }
| ExprTy::Negate(body) | ExprTy::Negate(body)
| ExprTy::Assign { val: body, .. } => body.ends_with_block(), | ExprTy::Assign { val: body, .. } => body.ends_with_block(),
| ExprTy::Define { val: body, .. } => body.ends_with_block(),
ExprTy::Fn(f) => f.ends_with_block(), ExprTy::Fn(f) => f.ends_with_block(),
_ => false, _ => false,
} }
} }
pub fn needs_semicolon(&self) -> bool {
!self.ends_with_block()
}
} }
-13
View File
@@ -2,7 +2,6 @@ use super::*;
pub struct Func { pub struct Func {
args: Vec<Param>, args: Vec<Param>,
name: Option<Ident>,
ret: Option<Type>, ret: Option<Type>,
body: Expr, body: Expr,
span: Span, span: Span,
@@ -10,14 +9,6 @@ pub struct Func {
impl Node for Func { impl Node for Func {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> { 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)?; ctx.expect(Token::OpenParen)?;
let args = ctx.list(Token::Comma, Token::CloseParen)?; let args = ctx.list(Token::Comma, Token::CloseParen)?;
let mut ret = None; let mut ret = None;
@@ -29,16 +20,12 @@ impl Node for Func {
args, args,
ret, ret,
body, body,
name,
span: ctx.span(), span: ctx.span(),
}) })
} }
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 {
write!(f, "fn")?; write!(f, "fn")?;
if let Some(name) = &self.name {
write!(f, " {name}")?;
}
write!(f, "(")?; write!(f, "(")?;
if let Some((last, rest)) = self.args.split_last() { if let Some((last, rest)) = self.args.split_last() {
for arg in rest { for arg in rest {
+1 -1
View File
@@ -9,7 +9,7 @@ impl Node for Ident {
fn parse(ctx: &mut super::ParseCtx) -> Result<Self, crate::io::CompilerMsg> { fn parse(ctx: &mut super::ParseCtx) -> Result<Self, crate::io::CompilerMsg> {
match ctx.expect_next()? { match ctx.expect_next()? {
Token::Ident(ident) => Ok(ctx.ident(ident)), Token::Ident(ident) => Ok(ctx.ident(ident)),
t => ctx.unexpected(&t, "an identifier"), t => ctx.unexpected(t, "an identifier"),
} }
} }
-2
View File
@@ -2,7 +2,6 @@ mod body;
mod expr; mod expr;
mod func; mod func;
mod ident; mod ident;
mod item;
mod param; mod param;
mod struct_; mod struct_;
mod ty; mod ty;
@@ -10,7 +9,6 @@ pub use body::*;
pub use expr::*; pub use expr::*;
pub use func::*; pub use func::*;
pub use ident::*; pub use ident::*;
pub use item::*;
pub use param::*; pub use param::*;
pub use ty::*; pub use ty::*;
+1 -1
View File
@@ -8,7 +8,7 @@ impl Node for Type {
fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> { fn parse(ctx: &mut ParseCtx) -> Result<Self, CompilerMsg> {
Ok(match ctx.expect_next()? { Ok(match ctx.expect_next()? {
Token::Ident(s) => Self::Ident(ctx.ident(s)), Token::Ident(s) => Self::Ident(ctx.ident(s)),
t => ctx.unexpected(&t, "a type")?, t => ctx.unexpected(t, "a type")?,
}) })
} }
+84 -12
View File
@@ -1,23 +1,95 @@
use crate::{ use std::{
io::CompilerOutput, collections::{HashMap, HashSet},
ir::{Ir, Namespace}, path::Path,
parser::{self, parse_file},
}; };
pub fn parse_program(path: &str, output: &mut CompilerOutput) -> Option<Ir> { use crate::{
io::{CompilerMsg, CompilerOutput, Span},
ir::Ir,
parser::{self, ExprTy, Ident, Node, parse_file},
};
pub fn parse_program(path: impl AsRef<Path>, output: &mut CompilerOutput) -> Option<Ir> {
let path = path.as_ref();
let mut imports = Imports::default();
let dir = path.parent().unwrap();
imports.add(path.file_stem().unwrap().to_str().unwrap());
while let Some(next) = imports.new.pop() {
imports.done.insert(next.clone());
let path = dir.join(next + ".lang");
println!("=== {path:?}");
let root = parse_file(path, output)?; let root = parse_file(path, output)?;
let mut ir = Ir::default(); print!("{}", root.new_dsp());
add_defs(ir.root(), &root); let defs = scan(&mut imports, &root, output);
for (name, spans) in &defs.duplicates {
output.error(CompilerMsg {
msg: format!("Multiple definitions found for {name}"),
spans: spans.clone(),
});
}
}
if !output.errors.is_empty() {
return None;
}
let ir = Ir::default();
Some(ir) Some(ir)
} }
pub fn add_defs(namespace: &mut Namespace, body: &parser::Body) { pub fn scan(imports: &mut Imports, body: &parser::Body, output: &mut CompilerOutput) -> Defs {
let mut defs = Defs::default();
for item in &body.items { for item in &body.items {
match &item.ty { match &item.ty {
parser::ItemTy::Let { name, ty, val } => todo!(), ExprTy::Define { target, const_, .. } if *const_ => match &target.ty {
parser::ItemTy::Fn(func) => todo!(), ExprTy::Ident(name) => defs.add(name),
parser::ItemTy::Expr(expr) => todo!(), _ => output.error(("Invalid left hand side of definition", target.span)),
parser::ItemTy::Import(ident) => todo!(), },
ExprTy::Import(import) => {
defs.add(import);
imports.add(&import.name);
} }
_ => (),
}
}
defs
}
#[derive(Default)]
pub struct Defs {
map: HashMap<String, (usize, Span)>,
duplicates: HashMap<String, Vec<Span>>,
next_id: usize,
}
impl Defs {
pub fn add(&mut self, ident: &Ident) {
if let Some(def) = self.map.get(&ident.name) {
if let Some(spans) = self.duplicates.get_mut(&ident.name) {
spans.push(ident.span);
} else {
self.duplicates
.insert(ident.name.clone(), vec![def.1, ident.span]);
}
return;
}
self.map
.insert(ident.name.clone(), (self.next_id, ident.span));
self.next_id += 1;
}
}
#[derive(Default)]
pub struct Imports {
done: HashSet<String>,
new: Vec<String>,
}
impl Imports {
pub fn add(&mut self, name: &str) {
if self.done.contains(name) || self.new.iter().any(|v| v == name) {
return;
}
self.new.push(name.to_string());
} }
} }
+9 -6
View File
@@ -1,14 +1,17 @@
modl other; x : i32 = 3;
let x: i32 = 3;
while true { while true {
print("hello"); print("hello");
print(x); print(x);
}; other.thing();
thing();
break;
}
let y = true; y :: true;
if y => print("hello"); if y => print("hello");
fn thing() { thing :: fn() {
} }
import other;
+3 -1
View File
@@ -1,3 +1,5 @@
fn thing() { thing :: fn() {
print("hello from other"); print("hello from other");
} }
import main;