Compare commits

...

2 commits

Author SHA1 Message Date
Jakub Doka bc2dd82eb7
welp 2024-11-17 21:25:22 +01:00
Jakub Doka aa2de502cc
saving 2024-11-17 21:09:36 +01:00
2 changed files with 255 additions and 289 deletions

View file

@ -34,13 +34,12 @@ pub use fs::*;
pub use utils::Ent;
use {
self::{
lexer::TokenKind,
parser::{idfl, CommentOr, Expr, ExprRef, Pos},
parser::{ExprRef, Pos},
ty::{ArrayLen, Builtin, Module},
utils::EntVec,
},
alloc::{string::String, vec::Vec},
core::{cell::Cell, fmt::Display, ops::Range},
core::{cell::Cell, ops::Range},
hashbrown::hash_map,
};
@ -1034,266 +1033,6 @@ pub struct Types {
tasks: Vec<Option<FTask>>,
}
// TODO: remove this
trait TypeParser {
fn tys(&mut self) -> &mut Types;
fn ty_display(&self, of: ty::Id) -> ty::Display;
fn on_reuse(&mut self, existing: ty::Id);
fn find_local_ty(&mut self, name: Ident) -> Option<ty::Id>;
fn eval_const(&mut self, file: Module, expr: &Expr, ty: ty::Id) -> u64;
fn eval_global(&mut self, file: Module, name: Ident, expr: &Expr) -> ty::Id;
fn infer_type(&mut self, expr: &Expr) -> ty::Id;
fn error(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id;
fn warn(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id;
fn find_type(
&mut self,
pos: Pos,
from_file: Module,
file: Module,
id: Result<Ident, &str>,
files: &[parser::Ast],
) -> ty::Id {
let ty = if let Ok(id) = id
&& let Some(ty) = self.find_local_ty(id)
{
ty
} else if let Ok(id) = id
&& let tys = self.tys()
&& let Some(&ty) = tys.syms.get(SymKey::Decl(file, id), &tys.ins)
{
self.on_reuse(ty);
ty
} else {
let f = &files[file.index()];
let Some((expr @ Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
return match id {
Ok(_) => {
debug_assert_eq!(from_file, file);
self.error(file, pos, "somehow this was not found")
}
Err("main") => self.error(
from_file,
pos,
format_args!(
"missing main function in '{}', compiler can't \
emmit libraries since such concept is not defined \
(minimal main function: `main := fn(): void {{}}`)",
f.path
),
),
Err(name) => {
self.error(from_file, pos, format_args!("undefined indentifier: {name}"))
}
};
};
let tys = self.tys();
let ty = if let Some(&ty) = tys.syms.get(SymKey::Decl(file, name), &tys.ins) {
ty
} else {
let (is_ct, ty) = left
.find_pattern_path(name, right, |right, is_ct| {
(
is_ct,
if is_ct && !matches!(right, Expr::Closure { .. }) {
self.tys()
.ins
.consts
.push(Const { ast: ExprRef::new(expr), name, file })
.into()
} else {
self.parse_ty(file, right, Some(name), files)
},
)
})
.unwrap_or_else(|_| unreachable!());
let tys = self.tys();
if let ty::Kind::Func(f) = ty.expand()
&& is_ct
{
tys.ins.funcs[f].is_inline = true;
}
tys.syms.insert(SymKey::Decl(file, name), ty, &tys.ins);
ty
};
if let Err(proper_case) = self.tys().case(ty)(f.ident_str(name)) {
self.warn(
from_file,
pos,
format_args!(
"the declaration does not have conventional \
casing, expected '{proper_case}', \
because the declared type is '{}'",
self.ty_display(ty),
),
);
}
ty
};
let tys = self.tys();
if let ty::Kind::Global(g) = ty.expand() {
let g = &tys.ins.globals[g];
if g.ty == ty::Id::TYPE {
return ty::Id::from(
u32::from_ne_bytes(g.data.as_slice().try_into().unwrap()) as u64
);
}
}
ty
}
/// returns none if comptime eval is required
fn parse_ty(
&mut self,
file: Module,
expr: &Expr,
name: Option<Ident>,
files: &[parser::Ast],
) -> ty::Id {
match *expr {
Expr::Mod { id, .. } => id.into(),
Expr::UnOp { op: TokenKind::Xor, val, .. } => {
let base = self.parse_ty(file, val, None, files);
self.tys().make_ptr(base)
}
Expr::UnOp { op: TokenKind::Que, val, .. } => {
let base = self.parse_ty(file, val, None, files);
self.tys().make_opt(base)
}
Expr::Ident { id, .. } if let Ok(bt) = ty::Builtin::try_from(id) => bt.into(),
Expr::Ident { id, pos, .. } => self.find_type(pos, file, file, Ok(id), files),
Expr::Field { target, pos, name }
if let ty::Kind::Module(inside) =
self.parse_ty(file, target, None, files).expand() =>
{
self.find_type(pos, file, inside, Err(name), files)
}
Expr::Directive { name: "TypeOf", args: [expr], .. } => self.infer_type(expr),
Expr::Slice { size: None, item, .. } => {
let ty = self.parse_ty(file, item, None, files);
self.tys().make_array(ty, ArrayLen::MAX)
}
Expr::Slice { size: Some(&Expr::Number { value, .. }), item, .. } => {
let ty = self.parse_ty(file, item, None, files);
self.tys().make_array(ty, value as _)
}
Expr::Slice { size, item, .. } => {
let ty = self.parse_ty(file, item, None, files);
let len = size
.map_or(ArrayLen::MAX, |expr| self.eval_const(file, expr, ty::Id::U32) as _);
self.tys().make_array(ty, len)
}
Expr::Struct { pos, fields, packed, captured, .. } => {
let captures_start = self.tys().tmp.args.len();
for &cp in captured {
let ty = self.find_local_ty(cp).expect("TODO");
self.tys().tmp.args.push(ty);
}
let captured = self.tys().pack_args(captures_start).expect("TODO");
let sym = SymKey::Struct(file, pos, captured);
let tys = self.tys();
if let Some(&ty) = tys.syms.get(sym, &tys.ins) {
return ty;
}
let prev_tmp = self.tys().tmp.struct_fields.len();
for field in fields.iter().filter_map(CommentOr::or) {
let ty = self.parse_ty(file, &field.ty, None, files);
let field = StructField { name: self.tys().names.intern(field.name), ty };
self.tys().tmp.struct_fields.push(field);
}
let tys = self.tys();
let ty = tys
.ins
.structs
.push(Struct {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.struct_fields.len() as _,
explicit_alignment: packed.then_some(1),
..Default::default()
})
.into();
tys.ins.struct_fields.extend(tys.tmp.struct_fields.drain(prev_tmp..));
tys.syms.insert(sym, ty, &tys.ins);
ty
}
Expr::Enum { pos, variants, .. } => {
let sym = SymKey::Enum(file, pos);
let tys = self.tys();
if let Some(&ty) = tys.syms.get(sym, &tys.ins) {
return ty;
}
let prev_tmp = self.tys().tmp.enum_fields.len();
for field in variants.iter().filter_map(CommentOr::or) {
let field = EnumField { name: self.tys().names.intern(field.name) };
self.tys().tmp.enum_fields.push(field);
}
let tys = self.tys();
let ty = tys
.ins
.enums
.push(Enum {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.enum_fields.len() as _,
})
.into();
tys.ins.enum_fields.extend(tys.tmp.enum_fields.drain(prev_tmp..));
tys.syms.insert(sym, ty, &tys.ins);
ty
}
Expr::Closure { pos, args, ret, .. } if let Some(name) = name => {
let func = Func {
file,
name,
sig: 'b: {
let arg_base = self.tys().tmp.args.len();
for arg in args {
let sym = parser::find_symbol(&files[file.index()].symbols, arg.id);
if sym.flags & idfl::COMPTIME != 0 {
self.tys().tmp.args.truncate(arg_base);
break 'b None;
}
let ty = self.parse_ty(file, &arg.ty, None, files);
self.tys().tmp.args.push(ty);
}
let Some(args) = self.tys().pack_args(arg_base) else {
return self.error(file, pos, "function has too many argumnets");
};
let ret = self.parse_ty(file, ret, None, files);
Some(Sig { args, ret })
},
expr: ExprRef::new(expr),
returns_type: matches!(ret, &Expr::Ident { id, .. } if ty::Builtin::try_from(id) == Ok(ty::Builtin::TYPE)),
..Default::default()
};
self.tys().ins.funcs.push(func).into()
}
_ if let Some(name) = name => self.eval_global(file, name, expr),
_ => ty::Id::from(self.eval_const(file, expr, ty::Id::TYPE)),
}
}
}
impl Types {
pub fn case(&self, ty: ty::Id) -> fn(&str) -> Result<(), &'static str> {
match ty.expand() {

View file

@ -10,12 +10,12 @@ use {
parser::{
self,
idfl::{self},
CtorField, Expr, MatchBranch, Pos,
CommentOr, CtorField, Expr, ExprRef, MatchBranch, Pos,
},
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
utils::{BitSet, Ent, Vc},
CompState, FTask, Func, Global, Ident, Offset, OffsetIter, OptLayout, Sig, StringRef,
SymKey, TypeParser, Types,
CompState, Const, Enum, EnumField, FTask, Func, Global, Ident, Offset, OffsetIter,
OptLayout, Sig, StringRef, Struct, StructField, SymKey, Types,
},
alloc::{string::String, vec::Vec},
core::{
@ -2488,7 +2488,7 @@ impl<'a> Codegen<'a> {
}
pub fn generate(&mut self, entry: Module) {
self.find_type(0, entry, entry, Err("main"), self.files);
self.find_type(0, entry, entry, Err("main"));
if self.tys.ins.funcs.is_empty() {
return;
}
@ -2732,7 +2732,7 @@ impl<'a> Codegen<'a> {
Some(Value::var(index).ty(var.ty))
}
Expr::Ident { id, pos, .. } => {
let decl = self.find_type(pos, self.ci.file, self.ci.file, Ok(id), self.files);
let decl = self.find_type(pos, self.ci.file, self.ci.file, Ok(id));
match decl.expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
@ -2877,7 +2877,7 @@ impl<'a> Codegen<'a> {
match self.tys.base_of(tty).unwrap_or(tty).expand() {
ty::Kind::Module(m) => {
match self.find_type(pos, self.ci.file, m, Err(name), self.files).expand() {
match self.find_type(pos, self.ci.file, m, Err(name)).expand() {
ty::Kind::NEVER => Value::NEVER,
ty::Kind::Global(global) => self.gen_global(global),
ty::Kind::Const(cnst) => self.gen_const(cnst, ctx),
@ -3894,7 +3894,8 @@ impl<'a> Codegen<'a> {
self.ci.ctrl.get()
};
self.close_if(lcntrl, rcntrl, then_scope)
self.close_if(lcntrl, rcntrl, then_scope)?;
Some(Value::VOID)
}
Expr::Match { pos, value, branches } => {
let value = self.expr(value)?;
@ -3983,9 +3984,9 @@ impl<'a> Codegen<'a> {
for (lcntrl, then_scope) in scopes.into_iter().rev() {
if let Some(v) = self.close_if(lcntrl, rcntrl, then_scope)
&& v.id != VOID
&& v != VOID
{
rcntrl = v.id;
rcntrl = v;
}
}
@ -4002,18 +4003,18 @@ impl<'a> Codegen<'a> {
}
}
fn close_if(&mut self, lcntrl: Nid, rcntrl: Nid, mut then_scope: Scope) -> Option<Value> {
fn close_if(&mut self, lcntrl: Nid, rcntrl: Nid, mut then_scope: Scope) -> Option<Nid> {
if lcntrl == Nid::MAX && rcntrl == Nid::MAX {
then_scope.clear(&mut self.ci.nodes);
return None;
} else if lcntrl == Nid::MAX {
then_scope.clear(&mut self.ci.nodes);
return Some(Value::VOID);
return Some(VOID);
} else if rcntrl == Nid::MAX {
self.ci.scope.clear(&mut self.ci.nodes);
self.ci.scope = then_scope;
self.ci.ctrl.set(lcntrl, &mut self.ci.nodes);
return Some(Value::VOID);
return Some(VOID);
}
self.ci.ctrl.set(
@ -4029,7 +4030,7 @@ impl<'a> Codegen<'a> {
self.tys,
);
then_scope.clear(&mut self.ci.nodes);
Some(Value::new(self.ci.ctrl.get()).ty(ty::Id::VOID))
Some(self.ci.ctrl.get())
}
fn gen_enum_variant(&mut self, pos: Pos, e: ty::Enum, intrnd: Option<Ident>) -> Option<Value> {
@ -4741,7 +4742,7 @@ impl<'a> Codegen<'a> {
}
fn ty_in(&mut self, file: Module, expr: &Expr) -> ty::Id {
self.parse_ty(file, expr, None, self.files)
self.parse_ty(file, expr, None)
}
fn ty_display(&self, ty: ty::Id) -> ty::Display {
@ -4988,16 +4989,6 @@ impl<'a> Codegen<'a> {
fn file(&self) -> &'a parser::Ast {
&self.files[self.ci.file.index()]
}
}
impl TypeParser for Codegen<'_> {
fn tys(&mut self) -> &mut Types {
self.tys
}
fn ty_display(&self, of: ty::Id) -> ty::Display {
self.ty_display(of)
}
fn eval_const(&mut self, file: Module, expr: &Expr, ret: ty::Id) -> u64 {
self.ct.activate();
@ -5066,13 +5057,13 @@ impl TypeParser for Codegen<'_> {
gid.into()
}
fn error(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id {
fn error_low(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id {
let mut buf = self.errors.borrow_mut();
write!(buf, "{}", self.files[file.index()].report(pos, msg)).unwrap();
ty::Id::NEVER
}
fn warn(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id {
fn warn_low(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id {
let mut buf = self.warnings.borrow_mut();
write!(buf, "(W) {}", self.files[file.index()].report(pos, msg)).unwrap();
ty::Id::NEVER
@ -5081,6 +5072,242 @@ impl TypeParser for Codegen<'_> {
fn find_local_ty(&mut self, ident: Ident) -> Option<ty::Id> {
self.ci.scope.vars.iter().rfind(|v| (v.id == ident && v.value() == NEVER)).map(|v| v.ty)
}
fn find_type(
&mut self,
pos: Pos,
from_file: Module,
file: Module,
id: Result<Ident, &str>,
) -> ty::Id {
let ty = if let Ok(id) = id
&& let Some(ty) = self.find_local_ty(id)
{
ty
} else if let Ok(id) = id
&& let Some(&ty) = self.tys.syms.get(SymKey::Decl(file, id), &self.tys.ins)
{
self.on_reuse(ty);
ty
} else {
let f = &self.files[file.index()];
let Some((expr @ Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
return match id {
Ok(_) => {
debug_assert_eq!(from_file, file);
self.error_low(file, pos, "somehow this was not found")
}
Err("main") => self.error_low(
from_file,
pos,
format_args!(
"missing main function in '{}', compiler can't \
emmit libraries since such concept is not defined \
(minimal main function: `main := fn(): void {{}}`)",
f.path
),
),
Err(name) => self.error_low(
from_file,
pos,
format_args!("undefined indentifier: {name}"),
),
};
};
let ty = if let Some(&ty) = self.tys.syms.get(SymKey::Decl(file, name), &self.tys.ins) {
ty
} else {
let (is_ct, ty) = left
.find_pattern_path(name, right, |right, is_ct| {
(
is_ct,
if is_ct && !matches!(right, Expr::Closure { .. }) {
self.tys
.ins
.consts
.push(Const { ast: ExprRef::new(expr), name, file })
.into()
} else {
self.parse_ty(file, right, Some(name))
},
)
})
.unwrap_or_else(|_| unreachable!());
if let ty::Kind::Func(f) = ty.expand()
&& is_ct
{
self.tys.ins.funcs[f].is_inline = true;
}
self.tys.syms.insert(SymKey::Decl(file, name), ty, &self.tys.ins);
ty
};
if let Err(proper_case) = self.tys.case(ty)(f.ident_str(name)) {
self.warn_low(
from_file,
pos,
format_args!(
"the declaration does not have conventional \
casing, expected '{proper_case}', \
because the declared type is '{}'",
self.ty_display(ty),
),
);
}
ty
};
if let ty::Kind::Global(g) = ty.expand() {
let g = &self.tys.ins.globals[g];
if g.ty == ty::Id::TYPE {
return ty::Id::from(
u32::from_ne_bytes(g.data.as_slice().try_into().unwrap()) as u64
);
}
}
ty
}
/// returns none if comptime eval is required
fn parse_ty(&mut self, file: Module, expr: &Expr, name: Option<Ident>) -> ty::Id {
match *expr {
Expr::Mod { id, .. } => id.into(),
Expr::UnOp { op: TokenKind::Xor, val, .. } => {
let base = self.parse_ty(file, val, None);
self.tys.make_ptr(base)
}
Expr::UnOp { op: TokenKind::Que, val, .. } => {
let base = self.parse_ty(file, val, None);
self.tys.make_opt(base)
}
Expr::Ident { id, .. } if let Ok(bt) = ty::Builtin::try_from(id) => bt.into(),
Expr::Ident { id, pos, .. } => self.find_type(pos, file, file, Ok(id)),
Expr::Field { target, pos, name }
if let ty::Kind::Module(inside) = self.parse_ty(file, target, None).expand() =>
{
self.find_type(pos, file, inside, Err(name))
}
Expr::Directive { name: "TypeOf", args: [expr], .. } => self.infer_type(expr),
Expr::Slice { size: None, item, .. } => {
let ty = self.parse_ty(file, item, None);
self.tys.make_array(ty, ArrayLen::MAX)
}
Expr::Slice { size: Some(&Expr::Number { value, .. }), item, .. } => {
let ty = self.parse_ty(file, item, None);
self.tys.make_array(ty, value as _)
}
Expr::Slice { size, item, .. } => {
let ty = self.parse_ty(file, item, None);
let len = size
.map_or(ArrayLen::MAX, |expr| self.eval_const(file, expr, ty::Id::U32) as _);
self.tys.make_array(ty, len)
}
Expr::Struct { pos, fields, packed, captured, .. } => {
let captures_start = self.tys.tmp.args.len();
for &cp in captured {
let ty = self.find_local_ty(cp).expect("TODO");
self.tys.tmp.args.push(ty);
}
let captured = self.tys.pack_args(captures_start).expect("TODO");
let sym = SymKey::Struct(file, pos, captured);
if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) {
return ty;
}
let prev_tmp = self.tys.tmp.struct_fields.len();
for field in fields.iter().filter_map(CommentOr::or) {
let ty = self.parse_ty(file, &field.ty, None);
let field = StructField { name: self.tys.names.intern(field.name), ty };
self.tys.tmp.struct_fields.push(field);
}
let ty = self
.tys
.ins
.structs
.push(Struct {
file,
pos,
name: name.unwrap_or_default(),
field_start: self.tys.ins.struct_fields.len() as _,
explicit_alignment: packed.then_some(1),
..Default::default()
})
.into();
self.tys.ins.struct_fields.extend(self.tys.tmp.struct_fields.drain(prev_tmp..));
self.tys.syms.insert(sym, ty, &self.tys.ins);
ty
}
Expr::Enum { pos, variants, .. } => {
let sym = SymKey::Enum(file, pos);
if let Some(&ty) = self.tys.syms.get(sym, &self.tys.ins) {
return ty;
}
let prev_tmp = self.tys.tmp.enum_fields.len();
for field in variants.iter().filter_map(CommentOr::or) {
let field = EnumField { name: self.tys.names.intern(field.name) };
self.tys.tmp.enum_fields.push(field);
}
let ty = self
.tys
.ins
.enums
.push(Enum {
file,
pos,
name: name.unwrap_or_default(),
field_start: self.tys.ins.enum_fields.len() as _,
})
.into();
self.tys.ins.enum_fields.extend(self.tys.tmp.enum_fields.drain(prev_tmp..));
self.tys.syms.insert(sym, ty, &self.tys.ins);
ty
}
Expr::Closure { pos, args, ret, .. } if let Some(name) = name => {
let func = Func {
file,
name,
sig: 'b: {
let arg_base = self.tys.tmp.args.len();
for arg in args {
let sym =
parser::find_symbol(&self.files[file.index()].symbols, arg.id);
if sym.flags & idfl::COMPTIME != 0 {
self.tys.tmp.args.truncate(arg_base);
break 'b None;
}
let ty = self.parse_ty(file, &arg.ty, None);
self.tys.tmp.args.push(ty);
}
let Some(args) = self.tys.pack_args(arg_base) else {
return self.error_low(file, pos, "function has too many argumnets");
};
let ret = self.parse_ty(file, ret, None);
Some(Sig { args, ret })
},
expr: ExprRef::new(expr),
returns_type: matches!(ret, &Expr::Ident { id, .. } if ty::Builtin::try_from(id) == Ok(ty::Builtin::TYPE)),
..Default::default()
};
self.tys.ins.funcs.push(func).into()
}
_ if let Some(name) = name => self.eval_global(file, name, expr),
_ => ty::Id::from(self.eval_const(file, expr, ty::Id::TYPE)),
}
}
}
#[cfg(test)]