going insane (not working yet)

This commit is contained in:
2025-04-26 16:46:04 -04:00
parent 3bf821d6b1
commit 71598a4afa
23 changed files with 472 additions and 291 deletions

View File

@@ -1,24 +1,45 @@
use std::{collections::HashMap, path::PathBuf};
pub type FileID = usize;
pub type FileMap = HashMap<FileID, SrcFile>;
#[derive(Debug, Clone)]
pub struct SrcFile {
pub path: PathBuf,
pub text: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FilePos {
pub file: FileID,
pub line: usize,
pub col: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FileSpan {
pub file: FileID,
pub start: FilePos,
pub end: FilePos,
}
impl FilePos {
pub fn start() -> Self {
Self { line: 0, col: 0 }
pub fn start(file: FileID) -> Self {
Self {
line: 0,
col: 0,
file,
}
}
}
impl FilePos {
pub fn to(self, end: FilePos) -> FileSpan {
FileSpan { start: self, end }
FileSpan {
start: self,
end,
file: self.file,
}
}
pub fn char_span(self) -> FileSpan {
FileSpan::at(self)
@@ -33,6 +54,7 @@ impl FileSpan {
Self {
start: pos,
end: pos,
file: pos.file,
}
}
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {

View File

@@ -1,4 +1,4 @@
use super::{FilePos, FileSpan};
use super::{FileMap, FilePos, FileSpan};
#[derive(Debug, Clone)]
pub struct CompilerMsg {
@@ -7,6 +7,7 @@ pub struct CompilerMsg {
}
pub struct CompilerOutput {
pub file_map: FileMap,
pub errs: Vec<CompilerMsg>,
pub hints: Vec<CompilerMsg>,
}
@@ -30,11 +31,18 @@ impl CompilerMsg {
spans: vec![FileSpan::at(pos)],
}
}
pub fn write_for(&self, ty: &str, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
pub fn write_to(
&self,
ty: &str,
writer: &mut impl std::io::Write,
map: &FileMap,
) -> std::io::Result<()> {
let after = if self.spans.is_empty() { "" } else { ":" };
writeln!(writer, "{}: {}{}", ty, self.msg, after)?;
for span in &self.spans {
span.write_for(writer, file)?;
let file = map.get(&span.file).expect("unknown file id");
writeln!(writer, "{:?}", &file.path)?;
span.write_for(writer, &file.text)?;
}
Ok(())
}
@@ -45,6 +53,7 @@ impl CompilerOutput {
Self {
errs: Vec::new(),
hints: Vec::new(),
file_map: FileMap::new(),
}
}
pub fn err(&mut self, msg: CompilerMsg) {
@@ -53,12 +62,12 @@ impl CompilerOutput {
pub fn hint(&mut self, msg: CompilerMsg) {
self.hints.push(msg);
}
pub fn write_for(&self, out: &mut impl std::io::Write, file: &str) {
pub fn write_to(&self, out: &mut impl std::io::Write) {
for err in &self.errs {
err.write_for("error", out, file).unwrap();
err.write_to("error", out, &self.file_map).unwrap();
}
for hint in &self.hints {
hint.write_for("hint", out, file).unwrap();
hint.write_to("hint", out, &self.file_map).unwrap();
}
}
}

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::ir::{AsmBlockArgType, Size, SymbolSpace, UFunc, UInstrInst, VarOffset};
use crate::ir::{AsmBlockArgType, Size, StructTy, SymbolSpace, UFunc, UInstrInst, VarOffset};
use super::{
IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, Type, UInstruction, UProgram, VarID,
@@ -17,7 +17,7 @@ impl LProgram {
pub fn create(p: &UProgram) -> Result<Self, String> {
let start = p
.names
.id::<UFunc>("crate")
.id::<UFunc>(&["crate".to_string()])
.ok_or("no start method found")?;
let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
let entry = ssbuilder.func(&start);
@@ -44,10 +44,20 @@ impl LProgram {
pub struct StructInst {
offsets: Vec<Len>,
types: Vec<Type>,
order: HashMap<String, usize>,
size: Size,
}
impl StructInst {
pub fn offset(&self, name: &str) -> Option<Len> {
Some(self.offsets[*self.order.get(name)?])
}
pub fn ty(&self, name: &str) -> Option<&Type> {
Some(&self.types[*self.order.get(name)?])
}
}
pub struct LFunctionBuilder<'a> {
data: LFunctionBuilderData<'a>,
program: &'a UProgram,
@@ -72,7 +82,7 @@ pub struct LFunctionBuilderData<'a> {
instrs: Vec<LInstruction>,
stack: HashMap<VarID, Size>,
subvar_map: HashMap<VarID, VarOffset>,
struct_insts: HashMap<Type, StructInst>,
struct_insts: HashMap<StructTy, StructInst>,
makes_call: bool,
loopp: Option<LoopCtx>,
}
@@ -258,6 +268,10 @@ impl<'a> LFunctionBuilder<'a> {
self.data.instrs.push(LInstruction::Ret { src })
}
UInstruction::Construct { dest, fields } => {
let sty = &self.program.expect(dest.id).ty;
let Type::Struct(sty) = sty else {
panic!("bruh htis aint' a struct");
};
self.alloc_stack(dest.id)?;
for (field, var) in fields {
self.map_subvar(var.id);
@@ -266,7 +280,7 @@ impl<'a> LFunctionBuilder<'a> {
src: var.id,
dest_offset: self
.data
.field_offset(self.program, dest.id, field)
.field_offset(self.program, sty, field)
.expect("field offset"),
src_offset: 0,
};
@@ -344,22 +358,25 @@ impl<'a> LFunctionBuilder<'a> {
impl LFunctionBuilderData<'_> {
pub fn var_offset(&mut self, p: &UProgram, var: VarID) -> Option<VarOffset> {
let mut current = VarOffset { id: var, offset: 0 };
while let Some(parent) = &p.get(current.id)?.parent {
current.id = parent.var;
current.offset += self.field_offset(p, parent.var, &parent.field)?;
while let Type::Member(parent) = &p.get(current.id)?.ty {
current.id = parent.parent;
// ... should it be set to 0 if not? what does that even mean??
// "the field of this struct is a reference to another variable" ????
if let Type::Struct(sty) = &p.get(parent.parent)?.ty {
current.offset += self.field_offset(p, sty, &parent.name)?;
}
}
Some(current)
}
pub fn addr_size(&self) -> Size {
64
}
pub fn struct_inst(&mut self, p: &UProgram, ty: &Type) -> Option<&StructInst> {
pub fn struct_inst(&mut self, p: &UProgram, ty: &StructTy) -> &StructInst {
// normally I'd let Some(..) here and return, but polonius does not exist :grief:
if self.struct_insts.get(ty).is_none() {
let Type::Struct { id, args } = ty else {
return None;
};
let struc = p.get(*id)?;
let StructTy { id, args } = ty;
let struc = p.expect(*id);
let mut types = Vec::new();
let mut sizes = struc
.fields
.iter()
@@ -374,6 +391,7 @@ impl LFunctionBuilderData<'_> {
} else {
&f.ty
};
types.push(ty.clone());
(n, self.size_of_type(p, ty).expect("unsized type"))
})
.collect::<Vec<_>>();
@@ -392,25 +410,24 @@ impl LFunctionBuilderData<'_> {
StructInst {
offsets,
order,
types,
size: offset,
},
);
}
self.struct_insts.get(ty)
self.struct_insts.get(ty).unwrap()
}
pub fn field_offset(&mut self, p: &UProgram, var: VarID, field: &str) -> Option<Len> {
let ty = &p.get(var)?.ty;
let inst = self.struct_inst(p, ty)?;
let i = *inst.order.get(field)?;
Some(*inst.offsets.get(i)?)
pub fn field_offset(&mut self, p: &UProgram, sty: &StructTy, field: &str) -> Option<Len> {
let inst = self.struct_inst(p, sty);
Some(inst.offset(field)?)
}
pub fn size_of_type(&mut self, p: &UProgram, ty: &Type) -> Option<Size> {
// TODO: target matters
Some(match ty {
Type::Bits(b) => *b,
Type::Struct { id, args } => self.struct_inst(p, ty)?.size,
Type::Struct(ty) => self.struct_inst(p, ty).size,
Type::Generic { id } => return None,
Type::Fn { args, ret } => todo!(),
Type::Ref(_) => self.addr_size(),
@@ -420,6 +437,18 @@ impl LFunctionBuilderData<'_> {
Type::Error => return None,
Type::Unit => 0,
Type::Placeholder => return None,
Type::Module(_) => return None,
Type::Member(m) => {
let pty = &p.expect(m.parent).ty;
let ty = match pty {
Type::Struct(ty) => self.struct_inst(p, ty).ty(&m.name)?,
Type::Module(path) => p.path_ty(path)?,
Type::Member(_) => return self.size_of_type(p, pty),
_ => return None,
}
.clone();
self.size_of_type(p, &ty)?
}
})
}

View File

@@ -20,16 +20,56 @@ impl OriginMap {
}
}
pub type NamePath = Vec<String>;
pub struct NameTree {
ids: [HashMap<String, usize>; NAMED_KINDS],
children: HashMap<String, NameTree>,
}
impl NameTree {
pub fn new() -> Self {
Self {
ids: core::array::from_fn(|_| HashMap::new()),
children: HashMap::new(),
}
}
pub fn get(&self, path: &[String]) -> Option<&NameTree> {
let first = path.first()?;
self.children.get(first)?.get(&path[1..])
}
pub fn id<K: Kind>(&self, path: &[String]) -> Option<ID<K>> {
let last = path.last()?;
self.get(&path[..path.len() - 1])?.ids[K::INDEX]
.get(last)
.copied()
.map(ID::new)
}
pub fn insert<K: Kind>(&mut self, path: &[String], id: usize) {
if let [key] = &path[..] {
self.ids[K::INDEX].insert(key.to_string(), id);
return;
}
let Some(key) = path.first() else {
return;
};
self.children
.entry(key.to_string())
.or_insert_with(|| NameTree::new())
.insert::<K>(&path[1..], id);
}
}
pub struct NameMap {
names: [Vec<String>; NAMED_KINDS],
inv_names: [HashMap<String, usize>; NAMED_KINDS],
tree: NameTree,
}
impl NameMap {
pub fn new() -> Self {
Self {
names: core::array::from_fn(|_| Vec::new()),
inv_names: core::array::from_fn(|_| HashMap::new()),
tree: NameTree::new(),
}
}
pub fn path<K: Kind>(&self, id: ID<K>) -> &str {
@@ -42,11 +82,13 @@ impl NameMap {
}
path
}
pub fn id<K: Kind>(&self, name: &str) -> Option<ID<K>> {
Some(ID::new(*self.inv_names[K::INDEX].get(name)?))
pub fn id<K: Kind>(&self, path: &[String]) -> Option<ID<K>> {
Some(self.tree.id(path)?)
}
pub fn push<K: Kind>(&mut self, name: String) {
self.inv_names[K::INDEX].insert(name.clone(), self.names[K::INDEX].len());
pub fn push<K: Kind>(&mut self, path: &[String]) {
let id = self.names[K::INDEX].len();
self.tree.insert::<K>(path, id);
let name = path.join("::");
self.names[K::INDEX].push(name);
}
}

View File

@@ -27,7 +27,6 @@ pub struct UGeneric {}
#[derive(Clone)]
pub struct UVar {
pub parent: Option<FieldRef>,
pub ty: Type,
}
@@ -37,12 +36,6 @@ pub struct VarOffset {
pub offset: Len,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FieldRef {
pub var: VarID,
pub field: String,
}
#[derive(Clone)]
pub struct UData {
pub ty: Type,
@@ -124,7 +117,7 @@ impl_kind!(UVar, 1, vars, "var");
impl_kind!(UStruct, 2, structs, "struct");
impl_kind!(UGeneric, 3, types, "type");
impl_kind!(UData, 4, data, "data");
pub const NAMED_KINDS: usize = 5;
pub const NAMED_KINDS: usize = 6;
pub type FnID = ID<UFunc>;
pub type VarID = ID<UVar>;
@@ -137,7 +130,6 @@ impl Finish for UFunc {
let var = p.def_searchable(
name.to_string(),
Some(UVar {
parent: None,
ty: Type::Placeholder,
}),
p.origins.get(id),

View File

@@ -71,19 +71,13 @@ impl UProgram {
pub fn get_fn_var(&self, id: VarID) -> Option<&UFunc> {
self.fns[self.fn_var.fun(id)?.0].as_ref()
}
pub fn temp_subvar(&mut self, origin: Origin, ty: Type, parent: FieldRef) -> VarInst {
self.temp_var_inner(origin, ty, Some(parent))
}
pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst {
self.temp_var_inner(origin, ty, None)
self.temp_var_inner(origin, ty)
}
fn temp_var_inner(&mut self, origin: Origin, ty: Type, parent: Option<FieldRef>) -> VarInst {
let v = self.def(
&format!("temp{}", self.temp),
Some(UVar { parent, ty }),
origin,
);
fn temp_var_inner(&mut self, origin: Origin, ty: Type) -> VarInst {
let v = self.def(&format!("temp{}", self.temp), Some(UVar { ty }), origin);
self.temp += 1;
VarInst {
id: v,
@@ -96,7 +90,7 @@ impl UProgram {
}
pub fn def<K: Kind + Finish>(&mut self, name: &str, k: Option<K>, origin: Origin) -> ID<K> {
self.names.push::<K>(self.path_for(name));
self.names.push::<K>(&self.path_for(name));
self.origins.push::<K>(origin);
let vec = K::from_program_mut(self);
let id = ID::new(vec.len());
@@ -105,12 +99,12 @@ impl UProgram {
id
}
pub fn path_for(&self, name: &str) -> String {
pub fn path_for(&self, name: &str) -> Vec<String> {
if self.path.is_empty() {
return name.to_string();
return vec![name.to_string()];
}
let mut path = self.path.join("::");
path = path + "::" + name;
let mut path = self.path.clone();
path.push(name.to_string());
path
}
@@ -125,27 +119,46 @@ impl UProgram {
id
}
pub fn ref_ty<'a>(&'a self, mem: &MemberRef) -> Option<&'a Type> {
self.follow_ref(mem).and_then(|r| Some(&self.get(r)?.ty))
}
pub fn follow_ref<'a>(&'a self, mem: &MemberRef) -> Option<VarID> {
let parent = self.get(mem.parent)?;
if let Type::Member(mem) = &parent.ty {
self.follow_ref(mem)
} else {
Some(mem.parent)
}
}
pub fn field_type<'a>(&'a self, sty: &'a Type, field: &str) -> Option<&'a Type> {
let Type::Struct { id, args } = sty else {
return None;
};
let struc = self.get(*id)?;
if let Type::Struct(st) = sty {
let struc = self.get(st.id)?;
let field = struc.fields.get(field)?;
if let Type::Generic { id } = field.ty {
for (i, g) in struc.generics.iter().enumerate() {
if *g == id {
return Some(&args[i]);
return Some(&st.args[i]);
}
}
}
Some(&field.ty)
} else if let Type::Module(path) = sty {
let id = self.names.id::<UVar>(path)?;
Some(&self.get(id)?.ty)
} else {
None
}
}
pub fn type_name(&self, ty: &Type) -> String {
let mut str = String::new();
match ty {
Type::Struct { id: base, args } => {
str += self.names.name(*base);
Type::Struct(ty) => {
let base = ty.id;
let args = &ty.args;
str += self.names.name(base);
if let Some(arg) = args.first() {
str = str + "<" + &self.type_name(arg);
}
@@ -178,9 +191,22 @@ impl UProgram {
Type::Unit => str += "()",
Type::Slice(t) => str += &format!("&[{}]", self.type_name(t)),
Type::Placeholder => str += "{placeholder}",
Type::Module(path) => str += &path.join("::"),
Type::Member(m) => {
str += &self
.ref_ty(m)
.map(|t| self.type_name(t))
.unwrap_or("{error}".to_string())
}
}
str
}
pub fn path_var(&self, path: &NamePath) -> Option<VarID> {
self.names.id(path)
}
pub fn path_ty(&self, path: &NamePath) -> Option<&Type> {
Some(&self.get(self.path_var(path)?)?.ty)
}
fn name_on_stack<K: Kind>(&mut self, id: ID<K>, name: String) {
let idx = self.name_stack.len() - 1;
let last = &mut self.name_stack[idx];

View File

@@ -1,16 +1,30 @@
use std::collections::HashMap;
use super::{GenericID, Len, StructID, UInstruction, UProgram, UVar, VarID};
use super::{assoc::NamePath, GenericID, Len, StructID, UInstruction, UProgram, UVar, VarID};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MemberRef {
pub parent: VarID,
pub name: String,
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct StructTy {
pub id: StructID,
pub args: Vec<Type>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Type {
Bits(u32),
Struct { id: StructID, args: Vec<Type> },
Struct(StructTy),
Generic { id: GenericID },
Fn { args: Vec<Type>, ret: Box<Type> },
Ref(Box<Type>),
Slice(Box<Type>),
Array(Box<Type>, Len),
Member(MemberRef),
Module(NamePath),
Infer,
Error,
Placeholder,
@@ -43,37 +57,16 @@ impl UProgram {
}
for (i, f) in self.iter_fns() {
let mut redo_iter = Vec::new();
let mut ph_vars = Vec::new();
let mut redo_new = Vec::new();
for i in f.flat_iter() {
if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) {
if self.resolve_instr_types(&mut vars, &i.i).is_none() {
redo_iter.push(i);
ph_vars.push(id);
}
}
while !redo_iter.is_empty() {
let mut new_ph = Vec::new();
for id in &ph_vars {
let i = id.0;
let Some(var) = &vars[i] else {
continue;
};
if var.ty == Type::Placeholder
&& let Some(parent) = var.parent.as_ref()
{
let pty = &vars[parent.var.0].as_ref().unwrap().ty;
if let Some(ft) = self.field_type(pty, &parent.field) {
vars[i].as_mut().unwrap().ty = ft.clone();
} else {
new_ph.push(parent.var);
}
}
}
ph_vars = new_ph;
for &i in &redo_iter {
if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) {
if self.resolve_instr_types(&mut vars, &i.i).is_none() {
redo_new.push(i);
ph_vars.push(id);
}
}
std::mem::swap(&mut redo_iter, &mut redo_new);
@@ -83,11 +76,7 @@ impl UProgram {
self.vars = vars;
}
pub fn resolve_instr_types(
&self,
vars: &mut [Option<UVar>],
i: &UInstruction,
) -> Result<(), VarID> {
pub fn resolve_instr_types(&self, vars: &mut [Option<UVar>], i: &UInstruction) -> Option<()> {
'outer: {
match &i {
UInstruction::Call { dest, f, args } => {
@@ -96,7 +85,7 @@ impl UProgram {
for (src, &dest) in args.iter().zip(&fun.args) {
let dest_ty = get(vars, dest)?;
let src_ty = get(vars, src.id)?;
if let Some(ty) = match_types(dest_ty, src_ty) {
if let Some(ty) = self.match_types(dest_ty, src_ty) {
set(vars, dest, ty.clone());
set(vars, src.id, ty);
}
@@ -105,7 +94,7 @@ impl UProgram {
UInstruction::Mv { dest, src } => {
let dest_ty = get(vars, dest.id)?;
let src_ty = get(vars, src.id)?;
if let Some(ty) = match_types(dest_ty, src_ty) {
if let Some(ty) = self.match_types(dest_ty, src_ty) {
set(vars, dest.id, ty.clone());
set(vars, src.id, ty);
}
@@ -116,7 +105,7 @@ impl UProgram {
let Type::Ref(dest_ty) = dest_ty else {
break 'outer;
};
if let Some(ty) = match_types(dest_ty, src_ty) {
if let Some(ty) = self.match_types(dest_ty, src_ty) {
set(vars, dest.id, ty.clone().rf());
set(vars, src.id, ty);
}
@@ -136,10 +125,10 @@ impl UProgram {
UInstruction::Ret { .. } => {}
UInstruction::Construct { dest, fields } => {
let dest_ty = get(vars, dest.id)?;
let Type::Struct { id, args } = dest_ty else {
let Type::Struct(sty) = dest_ty else {
break 'outer;
};
let id = *id;
let id = sty.id;
let Some(struc) = self.get(id) else {
break 'outer;
};
@@ -149,7 +138,7 @@ impl UProgram {
continue;
};
let src_ty = get(vars, src.id)?;
if let Some(ty) = match_types(&field.ty, src_ty) {
if let Some(ty) = self.match_types(&field.ty, src_ty) {
if let Type::Generic { id } = field.ty {
new.insert(id, ty.clone());
}
@@ -166,7 +155,7 @@ impl UProgram {
args[i] = ty;
}
}
set(vars, dest.id, Type::Struct { id, args });
set(vars, dest.id, Type::Struct(StructTy { id, args }));
}
UInstruction::If { cond, body: _ } => {}
UInstruction::Loop { body: _ } => {}
@@ -174,28 +163,10 @@ impl UProgram {
UInstruction::Continue => {}
}
}
Ok(())
}
Some(())
}
pub fn get(vars: &[Option<UVar>], id: VarID) -> Result<&Type, VarID> {
let var = vars[id.0]
.as_ref()
.expect("PARTIAL BORROWING WOULD BE REALLY COOL");
if var.ty == Type::Placeholder {
return Err(id);
}
Ok(&var.ty)
}
pub fn set(vars: &mut [Option<UVar>], id: VarID, ty: Type) {
vars[id.0]
.as_mut()
.expect("PARTIAL BORROWING WOULD BE REALLY COOL")
.ty = ty;
}
pub fn match_types(dest: &Type, src: &Type) -> Option<Type> {
pub fn match_types(&self, dest: &Type, src: &Type) -> Option<Type> {
if dest == src {
return None;
}
@@ -205,23 +176,14 @@ pub fn match_types(dest: &Type, src: &Type) -> Option<Type> {
(Type::Infer, x) | (x, Type::Infer) => Some(x.clone()),
// TODO: handle constraints?
(Type::Generic { id }, x) | (x, Type::Generic { id }) => Some(x.clone()),
(
Type::Struct {
id: dest_id,
args: dest_args,
},
Type::Struct {
id: src_id,
args: src_args,
},
) => {
if dest_id != src_id {
(Type::Struct(dest), Type::Struct(src)) => {
if dest.id != src.id {
return None;
}
let mut args = Vec::new();
let mut changed = false;
for (darg, sarg) in dest_args.iter().zip(src_args) {
if let Some(ty) = match_types(darg, sarg) {
for (darg, sarg) in dest.args.iter().zip(&src.args) {
if let Some(ty) = self.match_types(darg, sarg) {
args.push(ty);
changed = true;
} else if darg != sarg {
@@ -231,7 +193,7 @@ pub fn match_types(dest: &Type, src: &Type) -> Option<Type> {
}
}
if changed {
Some(Type::Struct { id: *dest_id, args })
Some(Type::Struct(StructTy { id: dest.id, args }))
} else {
None
}
@@ -249,14 +211,45 @@ pub fn match_types(dest: &Type, src: &Type) -> Option<Type> {
// TODO
None
}
(Type::Ref(dest), Type::Ref(src)) => Some(match_types(dest, src)?.rf()),
(Type::Slice(dest), Type::Slice(src)) => Some(match_types(dest, src)?.slice()),
(Type::Ref(dest), Type::Ref(src)) => Some(self.match_types(dest, src)?.rf()),
(Type::Slice(dest), Type::Slice(src)) => Some(self.match_types(dest, src)?.slice()),
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
if dlen != slen {
return None;
}
Some(match_types(dest, src)?.arr(*dlen))
Some(self.match_types(dest, src)?.arr(*dlen))
}
_ => None,
}
}
}
pub fn get(vars: &[Option<UVar>], id: VarID) -> Option<&Type> {
let mut var = vars[id.0]
.as_ref()
.expect("PARTIAL BORROWING WOULD BE REALLY COOL");
if var.ty == Type::Placeholder {
return None;
}
while let Type::Member(m) = &var.ty {
var = vars[m.parent.0].as_ref().expect("xd");
}
// x.y().z == a.b.c()
// 0 ------- member(1, z)
// 1 ----- call(2)
// 2 --- member(3, y)
// 3 - x
//
// 0 ------- call(1)
// 1 ----- member(c, 2)
// 2 --- member(b, 3)
// 3 - a
Some(&var.ty)
}
pub fn set(vars: &mut [Option<UVar>], id: VarID, ty: Type) {
vars[id.0]
.as_mut()
.expect("PARTIAL BORROWING WOULD BE REALLY COOL")
.ty = ty;
}

View File

@@ -3,14 +3,13 @@ use super::{Type, UInstrInst, UInstruction, UProgram};
use crate::common::{CompilerMsg, CompilerOutput, FileSpan};
impl UProgram {
pub fn validate(&self) -> CompilerOutput {
let mut output = CompilerOutput::new();
pub fn validate(&self, output: &mut CompilerOutput) {
for (id, f) in self.iter_fns() {
self.validate_fn(
&f.instructions,
self.origins.get(id),
&f.ret,
&mut output,
output,
true,
false,
);
@@ -46,7 +45,6 @@ impl UProgram {
}
}
}
output
}
pub fn validate_fn(

View File

@@ -7,13 +7,16 @@
pub const FILE_EXT: &str = "lang";
use common::{CompilerOutput, SrcFile};
use ir::{LProgram, UProgram};
use parser::{PModule, ParseResult, ParserCtx};
use parser::{Import, Imports, PModule, ParseResult, ParserCtx};
use std::{
collections::{HashMap, HashSet},
ffi::OsString,
fs::{create_dir_all, OpenOptions},
io::{stdout, BufRead, BufReader},
os::unix::fs::OpenOptionsExt,
path::Path,
path::{Path, PathBuf},
process::Command,
};
@@ -29,29 +32,63 @@ fn main() {
let gdb = std::env::args().nth(2).is_some_and(|a| a == "--debug");
let asm = std::env::args().nth(2).is_some_and(|a| a == "--asm");
if let Some(path) = file {
let file = std::fs::read_to_string(path).expect("failed to read file");
run_file(&file, gdb, asm);
let path = PathBuf::from(path);
run_file(&path, gdb, asm);
} else {
run_stdin();
}
}
fn run_file(file: &str, gdb: bool, asm: bool) {
let mut ctx = ParserCtx::from(file);
let res = PModule::parse(&mut ctx);
let mut output = ctx.output;
'outer: {
if !output.errs.is_empty() {
break 'outer;
impl UProgram {
pub fn from_path(path: &Path) -> (Self, CompilerOutput) {
let parent = path.parent().expect("bruh");
let mut program = Self::new();
let mut output = CompilerOutput::new();
let mut imports = Imports::new();
imports.insert(Import(
path.file_name()
.expect("bruh")
.to_str()
.expect("bruh")
.to_string(),
));
let mut imported = HashSet::new();
let mut fid = 0;
while !imports.is_empty() {
let iter = std::mem::take(&mut imports);
for i in iter {
let name = &i.0;
if imported.contains(&i) {
continue;
}
let path = parent.join(name).with_extension(FILE_EXT);
let text = std::fs::read_to_string(&path).expect("failed to read file");
output.file_map.insert(
fid,
SrcFile {
path,
text: text.clone(),
},
);
let mut ctx = ParserCtx::new(fid, text.as_str(), &mut output);
fid += 1;
let res = PModule::parse(&mut ctx);
// println!("Parsed:");
// println!("{:#?}", res.node);
let mut program = UProgram::new();
res.lower("crate".to_string(), &mut program, &mut output);
if !output.errs.is_empty() {
break 'outer;
res.lower(name.clone(), &mut program, &mut imports, &mut output);
imported.insert(i);
}
}
(program, output)
}
}
fn run_file(path: &Path, gdb: bool, asm: bool) {
let (mut program, mut output) = UProgram::from_path(path);
program.resolve_types();
program.validate(&mut output);
// println!("vars:");
// for (id, def) in program.iter_vars() {
// println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty));
@@ -59,11 +96,10 @@ fn run_file(file: &str, gdb: bool, asm: bool) {
// for (id, f) in program.iter_fns() {
// println!("{}:{id:?} = {:#?}", program.names.path(id), f);
// }
output = program.validate();
if !output.errs.is_empty() {
break 'outer;
output.write_to(&mut stdout());
return;
}
output.write_for(&mut stdout(), file);
let program = LProgram::create(&program).expect("morir");
let unlinked = compiler::compile(&program);
if asm {
@@ -73,8 +109,7 @@ fn run_file(file: &str, gdb: bool, asm: bool) {
println!("compiled");
save_run(&bin, gdb);
}
}
output.write_for(&mut stdout(), file);
output.write_to(&mut stdout());
}
fn save_run(binary: &[u8], run_gdb: bool) {

View File

@@ -1,13 +1,15 @@
use std::ops::{Deref, DerefMut};
use crate::common::FileID;
use super::{
MaybeParsable, Node, NodeParseResult, Parsable, ParsableWith, CompilerMsg, CompilerOutput,
CompilerMsg, CompilerOutput, MaybeParsable, Node, NodeParseResult, Parsable, ParsableWith,
TokenCursor,
};
pub struct ParserCtx<'a> {
pub cursor: TokenCursor<'a>,
pub output: CompilerOutput,
pub output: &'a mut CompilerOutput,
}
impl<'a> Deref for ParserCtx<'a> {
@@ -40,22 +42,10 @@ impl<'a> ParserCtx<'a> {
pub fn maybe_parse<T: MaybeParsable>(&mut self) -> Option<Node<T>> {
Node::maybe_parse(self)
}
}
impl<'a> From<TokenCursor<'a>> for ParserCtx<'a> {
fn from(cursor: TokenCursor<'a>) -> Self {
pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self {
Self {
cursor,
output: CompilerOutput::new(),
}
}
}
impl<'a> From<&'a str> for ParserCtx<'a> {
fn from(string: &'a str) -> Self {
Self {
cursor: TokenCursor::from(string),
output: CompilerOutput::new(),
cursor: TokenCursor::from_file_str(file, string),
output,
}
}
}

View File

@@ -1,3 +1,5 @@
use crate::common::FileID;
use super::{
token::{CharCursor, Keyword, Symbol, Token, TokenInstance},
CompilerMsg, FilePos,
@@ -17,7 +19,7 @@ impl<'a> TokenCursor<'a> {
self.next_start = next
.as_ref()
.map(|i| i.span.end)
.unwrap_or(FilePos::start());
.unwrap_or(FilePos::start(self.file()));
std::mem::replace(&mut self.next, next)
}
pub fn expect_next(&mut self) -> Result<TokenInstance, CompilerMsg> {
@@ -78,11 +80,11 @@ impl<'a> TokenCursor<'a> {
pub fn next_start(&self) -> FilePos {
self.next_start
}
pub fn from_file_str(id: FileID, string: &'a str) -> Self {
Self::from(CharCursor::from_file_str(id, string))
}
impl<'a> From<&'a str> for TokenCursor<'a> {
fn from(string: &'a str) -> Self {
Self::from(CharCursor::from(string))
pub fn file(&self) -> FileID {
self.cursor.file()
}
}
@@ -90,10 +92,10 @@ impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
fn from(mut cursor: CharCursor<'a>) -> Self {
let cur = TokenInstance::parse(&mut cursor);
Self {
next_start: FilePos::start(cursor.file()),
prev_end: FilePos::start(cursor.file()),
cursor,
next: cur,
next_start: FilePos::start(),
prev_end: FilePos::start(),
}
}
}

5
src/parser/v3/import.rs Normal file
View File

@@ -0,0 +1,5 @@
use std::collections::HashSet;
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Import(pub Vec<String>);
pub type Imports = HashSet<Import>;

View File

@@ -1,14 +1,13 @@
use crate::{
ir::{Type, UInstruction, VarInst},
ir::{Type, UInstruction, UVar, VarInst},
parser::{PConstStatement, PStatementLike},
};
use super::{import::Import, FnLowerCtx, FnLowerable, PBlock, PStatement};
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
impl FnLowerable for PBlock {
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
ctx.program.push();
let mut last = None;
let mut statements = Vec::new();
let mut fn_nodes = Vec::new();
@@ -29,12 +28,26 @@ impl FnLowerable for PBlock {
}
}
// then lower imports
for i in &import_nodes {
if let Some(i) = i.as_ref() {
let import = Import(i.0.clone());
ctx.imports.push(import);
for i_n in &import_nodes {
if let Some(i) = i_n.as_ref() {
let name = &i.0;
let import = Import(ctx.program.path_for(name));
ctx
.imports
.entry(import)
.or_insert(ctx.program.def(name, None, i_n.origin));
// I could prevent this if someone imports something twice,
// but that doesn't seem worth it at all
ctx.program.def_searchable::<UVar>(
name.clone(),
Some(UVar {
ty: Type::Module,
}),
i_n.origin,
);
}
}
ctx.program.push();
// then lower const things
let mut structs = Vec::new();
for s in &struct_nodes {

View File

@@ -15,7 +15,7 @@ impl Node<PVarDef> {
None => Type::Infer,
};
Some(VarInst {
id: program.def_searchable(name, Some(UVar { ty, parent: None }), self.origin),
id: program.def_searchable(name, Some(UVar { ty }), self.origin),
span: self.origin,
})
}

View File

@@ -1,6 +1,6 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
use crate::{
ir::{FieldRef, Type, UData, UInstruction, VarInst},
ir::{MemberRef, Type, UData, UInstruction, VarInst},
parser::PInfixOp,
};
@@ -69,13 +69,10 @@ impl FnLowerable for PExpr {
return None;
};
let fname = ident.as_ref()?.0.clone();
ctx.temp_subvar(
Type::Placeholder,
FieldRef {
var: res1.id,
field: fname,
},
)
ctx.temp(Type::Member(MemberRef {
parent: res1.id,
name: fname,
}))
}
PInfixOp::Assign => {
let res1 = e1.lower(ctx)?;

View File

@@ -1,6 +1,6 @@
use super::{import::Import, CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
use super::{Imports, CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
use crate::{
ir::{FieldRef, FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst},
ir::{MemberRef, FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst},
parser,
};
@@ -8,7 +8,13 @@ impl Node<PFunction> {
pub fn lower_name(&self, p: &mut UProgram) -> Option<FnID> {
self.as_ref()?.lower_name(p)
}
pub fn lower(&self, id: FnID, p: &mut UProgram, imports: &mut Vec<Import>, output: &mut CompilerOutput) {
pub fn lower(
&self,
id: FnID,
p: &mut UProgram,
imports: &mut Imports,
output: &mut CompilerOutput,
) {
if let Some(s) = self.as_ref() {
s.lower(id, p, imports, output)
}
@@ -22,7 +28,13 @@ impl PFunction {
let id = p.def_searchable(name.to_string(), None, self.header.origin);
Some(id)
}
pub fn lower(&self, id: FnID, p: &mut UProgram, imports: &mut Vec<Import>, output: &mut CompilerOutput) {
pub fn lower(
&self,
id: FnID,
p: &mut UProgram,
imports: &mut Imports,
output: &mut CompilerOutput,
) {
let name = p.names.name(id).to_string();
p.push_name(&name);
let (args, ret) = if let Some(header) = self.header.as_ref() {
@@ -69,7 +81,7 @@ pub struct FnLowerCtx<'a> {
pub instructions: Vec<UInstrInst>,
pub output: &'a mut CompilerOutput,
pub origin: FileSpan,
pub imports: &'a mut Vec<Import>
pub imports: &'a mut Imports,
}
impl FnLowerCtx<'_> {
@@ -103,9 +115,6 @@ impl FnLowerCtx<'_> {
pub fn temp(&mut self, ty: Type) -> VarInst {
self.program.temp_var(self.origin, ty)
}
pub fn temp_subvar(&mut self, ty: Type, parent: FieldRef) -> VarInst {
self.program.temp_subvar(self.origin, ty, parent)
}
pub fn push(&mut self, i: UInstruction) {
self.push_at(i, self.origin);
}

View File

@@ -1 +0,0 @@
pub struct Import(pub String);

View File

@@ -6,21 +6,26 @@ mod expr;
mod func;
mod struc;
mod ty;
mod import;
use super::*;
use crate::ir::{Type, UFunc, UProgram};
impl PModule {
pub fn lower(&self, name: String, p: &mut UProgram, output: &mut CompilerOutput) {
let id = p.def_searchable(name.clone(), None, self.block.origin);
pub fn lower(
&self,
name: String,
p: &mut UProgram,
imports: &mut Imports,
output: &mut CompilerOutput,
) {
let fid = p.def_searchable(name.clone(), None, self.block.origin);
p.push_name(&name);
let mut fctx = FnLowerCtx {
program: p,
instructions: Vec::new(),
output,
origin: self.block.origin,
imports: Vec::new(),
imports,
};
self.block.lower(&mut fctx);
let f = UFunc {
@@ -28,12 +33,13 @@ impl PModule {
instructions: fctx.instructions,
ret: Type::Unit,
};
p.write(id, f);
p.write(fid, f);
p.pop_name();
}
}
pub use func::FnLowerCtx;
use import::Imports;
pub trait FnLowerable {
type Output;

View File

@@ -1,5 +1,5 @@
use crate::{
ir::{GenericID, Type, UGeneric, UProgram, UStruct},
ir::{GenericID, StructTy, Type, UGeneric, UProgram, UStruct},
parser::PGenericDef,
};
@@ -24,7 +24,7 @@ impl PType {
Type::Generic { id }
} else if let Some(id) = ids.and_then(|ids| ids.get::<UStruct>()) {
let args = self.args.iter().map(|n| n.lower(p, output)).collect();
Type::Struct { id, args }
Type::Struct(StructTy { id, args })
} else if let Ok(num) = name.parse::<u32>() {
Type::Bits(num)
} else {

View File

@@ -6,6 +6,7 @@ mod node;
mod nodes;
mod parse;
mod token;
mod import;
use crate::common::{CompilerMsg, CompilerOutput, FileSpan, FilePos};
pub use ctx::*;
@@ -14,6 +15,7 @@ pub use node::*;
pub use nodes::*;
pub use parse::*;
pub use token::*;
pub use import::*;
// idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct
// creation, etc. instead of specializing at the parsing level

View File

@@ -133,7 +133,7 @@ impl<T: Parsable> ParsableWith for T {
impl<T: ParsableWith> Node<T> {
pub fn parse_with(ctx: &mut ParserCtx, data: T::Data) -> NodeParseResult<T> {
let start = ctx.peek().map(|t| t.span.start).unwrap_or(FilePos::start());
let start = ctx.peek().map(|t| t.span.start).unwrap_or(FilePos::start(ctx.cursor.file()));
let (inner, recover) = match T::parse(ctx, data) {
ParseResult::Ok(v) => (Some(v), false),
ParseResult::Recover(v) => (Some(v), true),

View File

@@ -1,14 +1,17 @@
use std::{iter::Peekable, str::Chars};
use crate::common::FileID;
use super::super::{CompilerMsg, FilePos};
pub struct CharCursor<'a> {
file: FileID,
chars: Peekable<Chars<'a>>,
next_pos: FilePos,
prev_pos: FilePos,
}
impl CharCursor<'_> {
impl<'a> CharCursor<'a> {
pub fn next(&mut self) -> Option<char> {
let res = self.peek()?;
self.advance();
@@ -54,14 +57,15 @@ impl CharCursor<'_> {
pub fn prev_pos(&self) -> FilePos {
self.prev_pos
}
}
impl<'a> From<&'a str> for CharCursor<'a> {
fn from(value: &'a str) -> Self {
pub fn from_file_str(file: FileID, value: &'a str) -> Self {
Self {
chars: value.chars().peekable(),
next_pos: FilePos::start(),
prev_pos: FilePos::start(),
next_pos: FilePos::start(file),
prev_pos: FilePos::start(file),
file,
}
}
pub fn file(&self) -> FileID {
self.file
}
}

View File

@@ -4,10 +4,10 @@ mod symbol;
use std::ops::Deref;
use super::FileSpan;
pub use cursor::*;
pub use keyword::*;
pub use symbol::*;
use super::FileSpan;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Token {
@@ -35,7 +35,11 @@ impl TokenInstance {
let end = cursor.prev_pos();
return Some(Self {
token: Token::Symbol(s),
span: FileSpan { start, end },
span: FileSpan {
start,
end,
file: cursor.file(),
},
});
}
let mut word = String::new();
@@ -54,7 +58,11 @@ impl TokenInstance {
};
Some(Self {
token,
span: FileSpan { start, end },
span: FileSpan {
start,
end,
file: cursor.file(),
},
})
}
}