a ton of stuff idk more ir work

This commit is contained in:
2024-10-22 02:30:50 -04:00
parent 14a4fb1ff9
commit 87f755b763
46 changed files with 1967 additions and 540 deletions

80
src/ir/file.rs Normal file
View File

@@ -0,0 +1,80 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FilePos {
pub line: usize,
pub col: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FileSpan {
pub start: FilePos,
pub end: FilePos,
}
impl FilePos {
pub fn start() -> Self {
Self { line: 0, col: 0 }
}
}
impl FilePos {
pub fn to(self, end: FilePos) -> FileSpan {
FileSpan { start: self, end }
}
pub fn char_span(self) -> FileSpan {
FileSpan::at(self)
}
}
const BEFORE: usize = 1;
const AFTER: usize = 0;
impl FileSpan {
pub fn at(pos: FilePos) -> Self {
Self {
start: pos,
end: pos,
}
}
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
let start = self.start.line.saturating_sub(BEFORE);
let num_before = self.start.line - start;
let mut lines = file.lines().skip(start);
let width = format!("{}", self.end.line + AFTER).len();
let same_line = self.start.line == self.end.line;
for i in 0..num_before {
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
}
let line = lines.next().unwrap();
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
let len = if same_line {
self.end.col - self.start.col + 1
} else {
line.len() - self.start.col
};
writeln!(
writer,
"{} | {}",
" ".repeat(width),
" ".repeat(self.start.col) + &"^".repeat(len)
)?;
if !same_line {
for _ in 0..self.end.line - self.start.line - 1 {
lines.next();
}
let line = lines.next().unwrap();
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
writeln!(
writer,
"{} | {}",
" ".repeat(width),
"^".repeat(self.end.col + 1)
)?;
}
// for i in 0..AFTER {
// if let Some(next) = lines.next() {
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
// }
// }
Ok(())
}
}

View File

@@ -1,278 +1,8 @@
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
mod namespace;
mod structure;
mod file;
use crate::parser::{
Body, Expr, FileSpan, Function, Ident, Literal, Module, ParserError, ParserErrors, Statement,
};
pub use namespace::*;
pub use structure::*;
pub use file::*;
#[derive(Debug)]
pub enum IRInstruction {
Li(Var, Literal),
Mv(Var, Var),
Not(Var, Var),
Noti(Var, Literal),
La(Var, IRIdent),
Call(FnIdent, Vec<Var>),
}
#[derive(Debug)]
pub struct IRFunction {
instructions: Vec<IRInstruction>,
}
impl Module {
pub fn lower(&self, map: &mut Namespace, errors: &mut ParserErrors) {
for f in &self.functions {
if let Some(f) = f.as_ref() {
f.lower(map, errors);
}
}
}
}
impl Function {
pub fn lower(&self, map: &mut Namespace, errors: &mut ParserErrors) {
let Some(name) = self.name.as_ref() else {
return;
};
if map.get(name).is_some() {
errors.add(ParserError {
msg: format!("Already something named '{:?}'", self.name),
spans: vec![self.name.span],
});
} else {
let f = map.reserve_fn(self.name.span);
let mut instructions = Vec::new();
if let Some(b) = self.body.as_ref() {
b.lower(map, &mut instructions, errors)
}
map.write_fn(name, f, IRFunction { instructions });
}
}
}
impl Body {
pub fn lower(
&self,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) {
let mut map = map.push();
for statement in &self.statements {
let Some(statement) = statement.as_ref() else {
continue;
};
match statement {
Statement::Let(name_node, expr) => {
let Some(name) = name_node.as_ref() else {
continue;
};
let Some(expr) = expr.as_ref() else {
continue;
};
let res = expr.lower(&mut map, instructions, errors);
let name = map.add_var(name, name_node.span);
if let Some(res) = res {
instructions.push(match res {
ExprResult::Lit(l) => IRInstruction::Li(name, l),
ExprResult::Var(i) => IRInstruction::Mv(name, i),
ExprResult::Fn(f) => todo!(),
});
}
}
Statement::Return(e) => todo!(),
Statement::Expr(expr) => {
expr.as_ref().map(|e| e.lower(&mut map, instructions, errors));
}
}
}
}
}
impl Expr {
pub fn lower(
&self,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) -> Option<ExprResult> {
match self {
Expr::Lit(l) => Some(ExprResult::Lit(l.as_ref()?.clone())),
Expr::Ident(i) => {
let Some(id) = map.get(i.as_ref()?) else {
errors.add(ParserError::identifier_not_found(i));
return None;
};
match id.ty() {
IdentTypeMatch::Var(var) => Some(ExprResult::Var(var)),
IdentTypeMatch::Fn(f) => Some(ExprResult::Fn(f)),
}
}
Expr::BinaryOp(_, _, _) => todo!(),
Expr::UnaryOp(op, e) => {
let res = e.as_ref()?.lower(&mut map, instructions, errors)?;
let res = match op {
crate::parser::UnaryOperator::Not => {
let temp = map.reserve_var(e.span);
match res {
ExprResult::Lit(l) => instructions.push(IRInstruction::Noti(temp, l)),
ExprResult::Var(i) => instructions.push(IRInstruction::Not(temp, i)),
ExprResult::Fn(_) => {
errors.add(ParserError::from_span(
e.span,
"Cannot call not on a function".to_string(),
));
return None;
}
}
temp
}
crate::parser::UnaryOperator::Ref => todo!(),
};
Some(ExprResult::Var(res))
}
Expr::Block(_) => todo!(),
Expr::Call(e, args) => {
let e = e.as_ref()?.lower(&mut map, instructions, errors);
let args = args.iter().map(|a| a.as_ref()?.lower(map, instructions, errors)).collect();
if let Some(r) = e {
let fun = match r {
ExprResult::Lit(literal) => todo!(),
ExprResult::Var(var) => todo!(),
ExprResult::Fn(f) => {
instructions.push(IRInstruction::Call(f, args));
},
};
} else {
todo!();
}
},
Expr::Group(e) => e.as_ref()?.lower(&mut map, instructions, errors),
}
}
}
pub enum ExprResult {
Lit(Literal),
Var(Var),
Fn(FnIdent),
}
#[derive(Debug)]
pub struct Namespace {
pub fns: Vec<Option<IRFunction>>,
pub vars: usize,
pub stack: Vec<HashMap<String, IRIdent>>,
}
impl Namespace {
pub fn new() -> Self {
Self {
fns: Vec::new(),
vars: 0,
stack: vec![HashMap::new()],
}
}
pub fn push(&mut self) -> NamespaceGuard {
self.stack.push(HashMap::new());
NamespaceGuard(self)
}
pub fn get(&self, name: &Ident) -> Option<IRIdent> {
for map in self.stack.iter().rev() {
let res = map.get(name.val());
if res.is_some() {
return res.copied();
}
}
None
}
pub fn reserve_var(&mut self, origin: FileSpan) -> Var {
let i = self.vars;
self.vars += 1;
Var(IRIdent {
origin,
ty: IdentType::Var,
i,
})
}
pub fn reserve_fn(&mut self, origin: FileSpan) -> FnIdent {
let i = self.fns.len();
self.fns.push(None);
FnIdent(IRIdent {
origin,
ty: IdentType::Fn,
i,
})
}
pub fn write_fn(&mut self, name: &Ident, id: FnIdent, f: IRFunction) -> IRIdent {
self.insert(name, id.0);
self.fns[id.0.i] = Some(f);
id.0
}
pub fn add_var(&mut self, name: &Ident, origin: FileSpan) -> Var {
let id = self.reserve_var(origin);
self.insert(name, id.0);
id
}
fn insert(&mut self, name: &Ident, id: IRIdent) {
let last = self.stack.len() - 1;
self.stack[last].insert(name.val().to_string(), id);
}
}
pub struct NamespaceGuard<'a>(&'a mut Namespace);
impl Drop for NamespaceGuard<'_> {
fn drop(&mut self) {
self.0.stack.pop();
}
}
impl Deref for NamespaceGuard<'_> {
type Target = Namespace;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for NamespaceGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug, Clone, Copy)]
pub struct IRIdent {
origin: FileSpan,
ty: IdentType,
i: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FnIdent(IRIdent);
#[derive(Debug, Clone, Copy)]
pub struct Var(IRIdent);
#[derive(Debug, Clone, Copy)]
pub enum IdentType {
Var,
Fn,
}
#[derive(Debug, Clone, Copy)]
pub enum IdentTypeMatch {
Var(Var),
Fn(FnIdent),
}
impl IRIdent {
pub fn ty(self) -> IdentTypeMatch {
match self.ty {
IdentType::Var => IdentTypeMatch::Var(Var(self)),
IdentType::Fn => IdentTypeMatch::Fn(FnIdent(self)),
}
}
}

241
src/ir/namespace.rs Normal file
View File

@@ -0,0 +1,241 @@
use std::{
collections::HashMap,
fmt::Debug,
ops::{Deref, DerefMut},
};
use super::{BuiltinType, FileSpan, FnDef, Function, Type, TypeDef, VarDef};
pub struct Namespace {
pub fn_defs: Vec<FnDef>,
pub var_defs: Vec<VarDef>,
pub type_defs: Vec<TypeDef>,
pub fns: Vec<Option<Function>>,
pub temp: usize,
pub stack: Vec<HashMap<String, Idents>>,
}
impl Namespace {
pub fn new() -> Self {
let mut s = Self {
fn_defs: Vec::new(),
var_defs: Vec::new(),
type_defs: Vec::new(),
fns: Vec::new(),
temp: 0,
stack: vec![HashMap::new()],
};
for b in BuiltinType::enumerate() {
s.def_type(b.def());
}
s
}
pub fn push(&mut self) -> NamespaceGuard {
self.stack.push(HashMap::new());
NamespaceGuard(self)
}
pub fn get(&self, name: &str) -> Option<Idents> {
for map in self.stack.iter().rev() {
let res = map.get(name);
if res.is_some() {
return res.cloned();
}
}
None
}
pub fn get_var(&self, id: VarIdent) -> &VarDef {
&self.var_defs[id.0]
}
pub fn get_fn(&self, id: FnIdent) -> &FnDef {
&self.fn_defs[id.0]
}
pub fn get_type(&self, id: TypeIdent) -> &TypeDef {
&self.type_defs[id.0]
}
pub fn alias_fn(&mut self, name: &str, id: FnIdent) {
self.insert(name, Ident::Fn(id));
}
pub fn name_var(&mut self, def: &VarDef, var: VarIdent) {
self.insert(&def.name, Ident::Var(var));
}
pub fn def_var(&mut self, var: VarDef) -> VarIdent {
let i = self.var_defs.len();
self.var_defs.push(var);
VarIdent(i)
}
pub fn temp_var(&mut self, origin: FileSpan, ty: Type) -> VarIdent {
let v = self.def_var(VarDef {
name: format!("temp{}", self.temp),
origin: super::Origin::File(origin),
ty,
});
self.temp += 1;
v
}
pub fn def_fn(&mut self, def: FnDef) -> FnIdent {
let i = self.fn_defs.len();
let id = FnIdent(i);
self.insert(&def.name, Ident::Fn(id));
self.fn_defs.push(def);
self.fns.push(None);
id
}
pub fn def_type(&mut self, def: TypeDef) -> TypeIdent {
let i = self.type_defs.len();
let id = TypeIdent(i);
self.insert(&def.name, Ident::Type(id));
self.type_defs.push(def);
id
}
pub fn type_name(&self, ty: &Type) -> String {
let mut str = String::new();
match ty {
Type::Concrete(t) => {
str += &self.get_type(*t).name;
}
Type::Generic { base, args } => {
str += &self.get_type(*base).name;
if let Some(arg) = args.first() {
str = str + "<" + &self.type_name(arg);
}
for arg in args.iter().skip(1) {
str = str + ", " + &self.type_name(arg);
}
if !args.is_empty() {
str += ">";
}
}
Type::Fn { args, ret } => {
str += "fn(";
if let Some(arg) = args.first() {
str += &self.type_name(arg);
}
for arg in args.iter().skip(1) {
str = str + ", " + &self.type_name(arg);
}
str += ") -> ";
str += &self.type_name(ret);
}
Type::Ref(t) => {
str = str + "&" + &self.type_name(t);
}
Type::Error => str += "{error}",
Type::Infer => str += "{inferred}",
}
str
}
fn insert(&mut self, name: &str, id: Ident) {
let idx = self.stack.len() - 1;
let last = &mut self.stack[idx];
if let Some(l) = last.get_mut(name) {
l.insert(id);
} else {
last.insert(name.to_string(), Idents::new(id));
}
}
pub fn write_fn(&mut self, id: FnIdent, f: Function) {
self.fns[id.0] = Some(f);
}
}
pub struct NamespaceGuard<'a>(&'a mut Namespace);
impl Drop for NamespaceGuard<'_> {
fn drop(&mut self) {
self.0.stack.pop();
}
}
impl Deref for NamespaceGuard<'_> {
type Target = Namespace;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl DerefMut for NamespaceGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
#[derive(Debug, Clone, Copy)]
pub enum Ident {
Var(VarIdent),
Fn(FnIdent),
Type(TypeIdent),
}
#[derive(Debug, Clone, Copy)]
pub struct Idents {
pub latest: Ident,
pub var: Option<VarIdent>,
pub func: Option<FnIdent>,
pub var_func: Option<VarOrFnIdent>,
pub ty: Option<TypeIdent>,
}
impl Idents {
fn new(latest: Ident) -> Self {
let mut s = Self {
latest,
var: None,
func: None,
var_func: None,
ty: None,
};
s.insert(latest);
s
}
fn insert(&mut self, i: Ident) {
self.latest = i;
match i {
Ident::Var(v) => {
self.var = Some(v);
self.var_func = Some(VarOrFnIdent::Var(v));
}
Ident::Fn(f) => {
self.func = Some(f);
self.var_func = Some(VarOrFnIdent::Fn(f));
}
Ident::Type(t) => self.ty = Some(t),
}
}
}
#[derive(Clone, Copy)]
pub struct TypeIdent(usize);
#[derive(Clone, Copy)]
pub struct VarIdent(usize);
#[derive(Clone, Copy)]
pub struct FnIdent(usize);
#[derive(Debug, Clone, Copy)]
pub enum VarOrFnIdent {
Var(VarIdent),
Fn(FnIdent),
}
impl TypeIdent {
pub fn builtin(ty: &BuiltinType) -> Self {
Self(*ty as usize)
}
}
impl Debug for VarIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v{}", self.0)
}
}
impl Debug for TypeIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "t{}", self.0)
}
}
impl Debug for FnIdent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "f{}", self.0)
}
}

226
src/ir/old.rs Normal file
View File

@@ -0,0 +1,226 @@
#[derive(Debug)]
pub enum IRInstruction {
Li(Var, Literal),
Mv(Var, Var),
Not(Var, Var),
Add(Var, Var, Var),
La(Var, IRIdent),
Call(Var, FnIdent, Vec<ExprResult>),
}
#[derive(Debug)]
pub struct IRFunction {
args: Vec<Var>,
instructions: Vec<IRInstruction>,
}
impl Module {
pub fn lower(&self, map: &mut Namespace, errors: &mut ParserErrors) {
let mut fns = Vec::new();
for f in &self.functions {
if let Some(f) = f.as_ref() {
if let Some(id) = f.reserve(map, errors) {
fns.push((id, f));
}
}
}
for (id, f) in fns {
f.lower(id, map, errors);
}
}
}
impl Function {
pub fn reserve(&self, map: &mut Namespace, errors: &mut ParserErrors) -> Option<FnIdent> {
let name = self.name.as_ref()?;
if let Some(other) = map.get(name) {
errors.add(ParserError {
msg: format!("Already {:?} named '{:?}'", other.ty, self.name),
spans: vec![self.name.span, other.origin],
});
None
} else {
Some(map.reserve_fn(name, self.name.span))
}
}
pub fn lower(
&self,
ident: FnIdent,
map: &mut Namespace,
errors: &mut ParserErrors,
) -> Option<()> {
let mut instructions = Vec::new();
let mut map = map.push();
let mut args = Vec::new();
for arg in &self.args {
args.push(map.def_var(arg.as_ref()?, arg.span)?);
}
if let Some(b) = self.body.as_ref() {
b.lower(&mut map, &mut instructions, errors)
}
map.write_fn(ident, IRFunction { instructions, args });
Some(())
}
}
impl Body {
pub fn lower(
&self,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) {
let mut map = map.push();
for statement in &self.statements {
let Some(statement) = statement.as_ref() else {
continue;
};
match statement {
Statement::Let(name_node, expr) => {
let Some(name) = name_node.as_ref() else {
continue;
};
let res = expr.lower(&mut map, instructions, errors);
if let Some(res) = res {
match res {
ExprResult::Var(v) => map.name_var(name, v),
ExprResult::Fn(f) => todo!(),
};
}
}
Statement::Return(e) => todo!(),
Statement::Expr(expr) => {
expr.lower(&mut map, instructions, errors);
}
}
}
}
}
impl Node<Box<Expr>> {
pub fn lower(
&self,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) -> Option<ExprResult> {
self.as_ref()?.lower(self.span, map, instructions, errors)
}
}
impl Node<Expr> {
pub fn lower(
&self,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) -> Option<ExprResult> {
self.as_ref()?.lowerr(self.span, map, instructions, errors)
}
}
impl Expr {
pub fn lowerr(
&self,
span: FileSpan,
map: &mut Namespace,
instructions: &mut Vec<IRInstruction>,
errors: &mut ParserErrors,
) -> Option<ExprResult> {
match self {
Expr::Lit(l) => {
let temp = map.temp_var(span);
instructions.push(IRInstruction::Li(temp, l.as_ref()?.clone()));
Some(ExprResult::Var(temp))
},
Expr::Ident(i) => {
let Some(id) = map.get(i.as_ref()?) else {
errors.add(ParserError::identifier_not_found(i));
return None;
};
match id.ty() {
IdentTypeMatch::Var(var) => Some(ExprResult::Var(var)),
IdentTypeMatch::Fn(f) => Some(ExprResult::Fn(f)),
}
}
Expr::BinaryOp(op, e1, e2) => {
let res1 = e1.lower(map, instructions, errors)?;
let res2 = e2.lower(map, instructions, errors)?;
let (ExprResult::Var(v1), ExprResult::Var(v2)) = (res1, res2) else {
errors.add(ParserError::from_span(span, "Cannot operate on functions".to_string()));
return None;
};
let temp = map.temp_var(span);
match op {
crate::parser::BinaryOperator::Add => {
instructions.push(IRInstruction::Add(temp, v1, v2))
}
crate::parser::BinaryOperator::Sub => todo!(),
crate::parser::BinaryOperator::Mul => todo!(),
crate::parser::BinaryOperator::Div => todo!(),
crate::parser::BinaryOperator::LessThan => todo!(),
crate::parser::BinaryOperator::GreaterThan => todo!(),
crate::parser::BinaryOperator::Access => todo!(),
crate::parser::BinaryOperator::Assign => todo!(),
}
Some(ExprResult::Var(temp))
}
Expr::UnaryOp(op, e) => {
let res = e.lower(map, instructions, errors)?;
let res = match op {
crate::parser::UnaryOperator::Not => {
let temp = map.temp_var(span);
match res {
ExprResult::Var(i) => instructions.push(IRInstruction::Not(temp, i)),
ExprResult::Fn(_) => {
errors.add(ParserError::from_span(
span,
"Cannot call not on a function".to_string(),
));
return None;
}
}
temp
}
crate::parser::UnaryOperator::Ref => todo!(),
};
Some(ExprResult::Var(res))
}
Expr::Block(_) => todo!(),
Expr::Call(e, args) => {
let fe = e.lower(map, instructions, errors);
let mut nargs = Vec::new();
for arg in args.iter() {
let arg = arg.lower(map, instructions, errors)?;
nargs.push(arg);
}
if let Some(r) = fe {
match r {
ExprResult::Fn(f) => {
let temp = map.temp_var(span);
instructions.push(IRInstruction::Call(temp, f, nargs));
Some(ExprResult::Var(temp))
}
o => {
errors.add(ParserError::from_span(
span,
"Expected function".to_string(),
));
None
}
}
} else {
None
}
}
Expr::Group(e) => e.lower(map, instructions, errors),
}
}
}
#[derive(Debug)]
pub enum ExprResult {
Var(Var),
Fn(FnIdent),
}

37
src/ir/structure/def.rs Normal file
View File

@@ -0,0 +1,37 @@
use super::{FileSpan, Type};
use std::fmt::Debug;
pub struct FnDef {
pub name: String,
pub args: Vec<VarDef>,
pub ret: Type,
pub origin: Origin,
}
pub struct TypeDef {
pub name: String,
pub args: usize,
pub origin: Origin,
}
#[derive(Clone)]
pub struct VarDef {
pub name: String,
pub ty: Type,
pub origin: Origin,
}
#[derive(Debug, Clone, Copy)]
pub enum Origin {
Builtin,
File(FileSpan),
}
impl FnDef {
pub fn ty(&self) -> Type {
Type::Fn {
args: self.args.iter().map(|a| a.ty.clone()).collect(),
ret: Box::new(self.ret.clone()),
}
}
}

56
src/ir/structure/func.rs Normal file
View File

@@ -0,0 +1,56 @@
use crate::compiler::riscv64::AsmInstruction;
use super::{FnIdent, VarIdent};
#[derive(Debug)]
pub struct Function {
instructions: Vec<Instruction>,
}
#[derive(Debug)]
pub enum Instruction {
Mv {
dest: VarIdent,
src: VarIdent,
},
Ref {
dest: VarIdent,
src: VarIdent,
},
Lf {
dest: VarIdent,
src: FnIdent,
},
Call {
dest: VarIdent,
f: FnIdent,
args: Vec<VarIdent>,
},
AsmBlock {
instructions: Vec<AsmInstruction>,
},
Ret {
src: VarIdent,
},
}
pub struct Instructions {
vec: Vec<Instruction>,
}
impl Function {
pub fn new(instructions: Instructions) -> Self {
Self {
instructions: instructions.vec,
}
}
}
impl Instructions {
pub fn new() -> Self {
Self { vec: Vec::new() }
}
pub fn push(&mut self, i: Instruction) {
self.vec.push(i);
}
}

9
src/ir/structure/mod.rs Normal file
View File

@@ -0,0 +1,9 @@
mod def;
mod func;
mod ty;
use super::*;
pub use def::*;
pub use func::*;
pub use ty::*;

35
src/ir/structure/ty.rs Normal file
View File

@@ -0,0 +1,35 @@
use super::{Origin, TypeDef, TypeIdent};
#[derive(Clone)]
pub enum Type {
Concrete(TypeIdent),
Generic { base: TypeIdent, args: Vec<Type> },
Fn { args: Vec<Type>, ret: Box<Type> },
Ref(Box<Type>),
Infer,
Error,
}
#[repr(usize)]
#[derive(Debug, Clone, Copy)]
pub enum BuiltinType {
Unit,
}
impl BuiltinType {
pub fn enumerate() -> &'static [Self; 1] {
&[Self::Unit]
}
pub fn def(&self) -> TypeDef {
match self {
BuiltinType::Unit => TypeDef {
name: "()".to_string(),
args: 0,
origin: Origin::Builtin,
},
}
}
pub fn id(&self) -> TypeIdent {
TypeIdent::builtin(self)
}
}