cl-typeck: More type inference

- Renamed "intrinsic" -> "primitive"
  - I thought it was funny, but it's just annoying.
- Rename "Uninferred" -> "Inferred"
  - It's a concrete type, but an inferred one.
- Add lifetimes to the Entry API
- Categorize does not care about recursive types. You can't have a recursive AST.
- Added helpful constructors to the inference engine
- Added some overloadable operators to the inference engine
  - These are a MAJOR work in progress
- Reorganized the Inference implementation file by functionality.

stdlib:
- Updated standard library.
This commit is contained in:
John 2025-04-22 06:33:57 -04:00
parent 7cf485fade
commit 681fbc88d3
16 changed files with 925 additions and 479 deletions

View File

@ -21,9 +21,10 @@ impl Path {
}
}
/// Gets the defining [Sym] of this path
pub fn as_sym(&self) -> Option<Sym> {
match self.parts.as_slice() {
[PathPart::Ident(name)] => Some(*name),
[.., PathPart::Ident(name)] => Some(*name),
_ => None,
}
}

View File

@ -80,6 +80,7 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
"q" | "query" => query_type_expression(prj)?,
"r" | "resolve" => resolve_all(prj)?,
"s" | "strings" => print_strings(),
"a" | "all" => infer_all(prj)?,
"t" | "test" => infer_expression(prj)?,
"h" | "help" | "" => {
println!(
@ -159,6 +160,7 @@ fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
})
}
#[allow(dead_code)]
fn infer_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ex>", "!?>", |line| {
if line.trim().is_empty() {
@ -238,6 +240,24 @@ fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn infer_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
for (id, error) in InferenceEngine::new(table, table.root()).infer_all() {
match error {
InferenceError::Mismatch(a, b) => {
eprint!("Mismatched types: {}, {}", table.entry(a), table.entry(b));
}
InferenceError::Recursive(a, b) => {
eprint!("Recursive types: {}, {}", table.entry(a), table.entry(b));
}
e => eprint!("{e}"),
}
eprintln!(" in {} ({id})", id.to_entry(table))
}
println!("...Inferred!");
Ok(())
}
fn list_types(table: &mut Table) {
for handle in table.debug_entry_iter() {
let id = handle.id();

View File

@ -46,15 +46,15 @@ impl<'t, 'a> Entry<'t, 'a> {
self.id
}
pub fn inner(&self) -> &Table<'a> {
pub fn inner(&self) -> &'t Table<'a> {
self.table
}
pub const fn with_id(&self, id: Handle) -> Entry<'_, 'a> {
pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
Self { table: self.table, id }
}
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'_, 'a>> {
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
}
@ -62,19 +62,19 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.root()
}
pub fn kind(&self) -> Option<&NodeKind> {
pub fn kind(&self) -> Option<&'t NodeKind> {
self.table.kind(self.id)
}
pub fn parent(&self) -> Option<Entry<'_, 'a>> {
pub fn parent(&self) -> Option<Entry<'t, 'a>> {
Some(Entry { id: *self.table.parent(self.id)?, ..*self })
}
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
pub fn children(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.children(self.id)
}
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> {
pub fn imports(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.imports(self.id)
}
@ -82,11 +82,11 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.body(self.id)
}
pub fn ty(&self) -> Option<&TypeKind> {
pub fn ty(&self) -> Option<&'t TypeKind> {
self.table.ty(self.id)
}
pub fn span(&self) -> Option<&Span> {
pub fn span(&self) -> Option<&'t Span> {
self.table.span(self.id)
}
@ -94,7 +94,7 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.meta(self.id)
}
pub fn source(&self) -> Option<&Source<'a>> {
pub fn source(&self) -> Option<&'t Source<'a>> {
self.table.source(self.id)
}

View File

@ -18,10 +18,10 @@ impl fmt::Display for Entry<'_, '_> {
if let Some(ty) = self.ty() {
match ty {
TypeKind::Uninferred => write!(f, "<_{}>", self.id),
TypeKind::Inferred => write!(f, "<_{}>", self.id),
TypeKind::Variable => write!(f, "<?{}>", self.id),
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Intrinsic(kind) => write!(f, "{kind}"),
TypeKind::Primitive(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(id) => {
f.write_str("&")?;

View File

@ -13,9 +13,9 @@ use cl_ast::*;
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
if let Some(meta) = table.meta(node) {
for meta @ Meta { name, kind } in meta {
if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
if let ("lang", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
let kind =
TypeKind::Intrinsic(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?);
TypeKind::Primitive(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?);
table.set_ty(node, kind);
return Ok(());
}
@ -206,7 +206,6 @@ type CatResult<T> = Result<T, Error>;
#[derive(Clone, Debug)]
pub enum Error {
BadMeta(Meta),
Recursive(Handle),
TypeEval(TypeEval, &'static str),
}
@ -219,10 +218,7 @@ impl From<TypeEval> for Error {
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::BadMeta(meta) => write!(f, "Unknown meta attribute: #[{meta}]"),
Error::Recursive(id) => {
write!(f, "Encountered recursive type without indirection: {id}")
}
Error::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
Error::TypeEval(e, during) => write!(f, "{e}{during}"),
}
}

View File

@ -2,9 +2,10 @@ use super::error::InferenceError;
use crate::{
entry::Entry,
handle::Handle,
stage::infer::inference::Inference,
table::{NodeKind, Table},
type_expression::TypeExpression,
type_kind::{Adt, TypeKind},
type_kind::{Adt, Primitive, TypeKind},
};
use cl_ast::Sym;
@ -28,37 +29,91 @@ use cl_ast::Sym;
*/
pub struct InferenceEngine<'table, 'a> {
pub(crate) at: Handle,
pub(super) table: &'table mut Table<'a>,
/// The current working node
pub(crate) at: Handle,
/// The current breakset
pub(crate) bset: Handle,
/// The current returnset
pub(crate) rset: Handle,
}
impl<'table, 'a> InferenceEngine<'table, 'a> {
/// Infers the type of an object by deferring to [`Inference::infer()`]
pub fn infer(&mut self, inferrable: &'a impl Inference<'a>) -> Result<Handle, InferenceError> {
inferrable.infer(self)
}
/// Constructs a new [`InferenceEngine`], scoped around a [`Handle`] in a [`Table`].
pub fn new(table: &'table mut Table<'a>, at: Handle) -> Self {
let never = table.anon_type(TypeKind::Never);
Self { at, table, bset: never, rset: never }
}
/// Constructs an [`InferenceEngine`] that borrows the same table as `self`,
/// but with a shortened lifetime.
pub fn scoped(&mut self) -> InferenceEngine<'_, 'a> {
InferenceEngine { at: self.at, table: self.table, bset: self.bset, rset: self.rset }
}
pub fn infer_all(&mut self) -> Vec<(Handle, InferenceError)> {
let iter = self.table.handle_iter();
let mut res = Vec::new();
for handle in iter {
let mut eng = self.at(handle);
// TODO: use sources instead of bodies, and infer the type globally
let Some(body) = eng.table.body(handle) else {
continue;
};
eprintln!("Evaluating body {body}");
match body.infer(&mut eng) {
Ok(ty) => println!("=> {}", eng.table.entry(ty)),
Err(e) => {
match &e {
&InferenceError::Mismatch(a, b) => {
eprintln!(
"=> Mismatched types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
&InferenceError::Recursive(a, b) => {
eprintln!(
"=> Recursive types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
e => eprintln!("=> {e}"),
}
res.push((handle, e))
}
}
}
res
}
/// Constructs a new InferenceEngine with the
pub fn at(&mut self, at: Handle) -> InferenceEngine<'_, 'a> {
InferenceEngine { at, table: self.table, bset: self.bset, rset: self.rset }
InferenceEngine { at, ..self.scoped() }
}
pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> {
let bset = self.new_var();
InferenceEngine { at: self.at, table: self.table, bset, rset: self.rset }
InferenceEngine { bset: self.new_var(), ..self.scoped() }
}
pub fn open_rset(&mut self) -> InferenceEngine<'_, 'a> {
let rset = self.new_var();
InferenceEngine { at: self.at, table: self.table, bset: self.bset, rset }
InferenceEngine { rset: self.new_var(), ..self.scoped() }
}
/// Constructs an [Entry] out of a [Handle], for ease of use
pub fn entry(&self, of: Handle) -> Entry<'_, 'a> {
self.table.entry(of)
}
#[deprecated = "Use dedicated methods instead."]
pub fn from_type_kind(&mut self, kind: TypeKind) -> Handle {
// TODO: preserve type heirarchy (for, i.e., reference types)
self.table.anon_type(kind)
}
@ -79,16 +134,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.table.anon_type(TypeKind::Instance(of))
}
pub fn de_inst(&self, to: Handle) -> Handle {
/// Gets the defining usage of a type without collapsing intermediates
pub fn def_usage(&self, to: Handle) -> Handle {
match self.table.entry(to).ty() {
Some(TypeKind::Instance(id)) => self.de_inst(*id),
Some(TypeKind::Instance(id)) => self.def_usage(*id),
_ => to,
}
}
/// Creates a new type variable representing a function (signature)
pub fn new_fn(&mut self, args: Handle, rety: Handle) -> Handle {
self.table.anon_type(TypeKind::FnSig { args, rety })
pub fn get_fn(&self, at: Handle, name: Sym) -> Option<(Handle, Handle)> {
use cl_ast::PathPart;
if let Some(&TypeKind::FnSig { args, rety }) = self
.entry(at)
.nav(&[PathPart::Ident(name)])
.as_ref()
.and_then(Entry::ty)
{
Some((args, rety))
} else {
None
}
}
/// Creates a new type variable representing a tuple
@ -101,11 +166,70 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
self.table.anon_type(TypeKind::Array(ty, size))
}
/// Creates a new type variable representing a slice of contiguous memory
pub fn new_slice(&mut self, ty: Handle) -> Handle {
self.table.anon_type(TypeKind::Slice(ty))
}
/// Creates a new reference to a type
pub fn new_ref(&mut self, to: Handle) -> Handle {
self.table.anon_type(TypeKind::Ref(to))
}
/// All primitives must be predefined in the standard library.
pub fn primitive(&self, name: Sym) -> Option<Handle> {
// TODO: keep a map of primitives in the table root
self.table.get_by_sym(self.table.root(), &name)
}
pub fn never(&mut self) -> Handle {
self.table.anon_type(TypeKind::Never)
}
pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Empty)
}
pub fn bool(&self) -> Handle {
self.primitive("bool".into())
.expect("There should be a type named bool.")
}
pub fn char(&self) -> Handle {
self.primitive("char".into())
.expect("There should be a type named char.")
}
pub fn str(&self) -> Handle {
self.primitive("str".into())
.expect("There should be a type named str.")
}
pub fn u32(&self) -> Handle {
self.primitive("u32".into())
.expect("There should be a type named u32.")
}
pub fn usize(&self) -> Handle {
self.primitive("usize".into())
.expect("There should be a type named usize.")
}
/// Creates a new inferred-integer literal
pub fn integer_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Local);
self.table
.set_ty(h, TypeKind::Primitive(Primitive::Integer));
h
}
/// Creates a new inferred-float literal
pub fn float_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Local);
self.table.set_ty(h, TypeKind::Primitive(Primitive::Float));
h
}
/// Enters a new scope
pub fn local_scope(&mut self) {
let scope = self.table.new_entry(self.at, NodeKind::Local);
@ -124,13 +248,16 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
pub fn set_instance(&mut self, to: Handle, of: Handle) {
let mut e = self.table.entry_mut(to);
match e.as_ref().ty() {
Some(TypeKind::Uninferred) => {
Some(TypeKind::Inferred) => {
if let Some(ty) = self.table.ty(of) {
self.table.set_ty(to, ty.clone());
}
None
}
Some(TypeKind::Variable) => e.set_ty(TypeKind::Instance(of)),
Some(TypeKind::Variable)
| Some(TypeKind::Primitive(Primitive::Float | Primitive::Integer)) => {
e.set_ty(TypeKind::Instance(of))
}
other => todo!("Cannot set {} to instance of: {other:?}", e.as_ref()),
};
}
@ -142,11 +269,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
return false;
};
match ty {
TypeKind::Uninferred => false,
TypeKind::Inferred => false,
TypeKind::Variable => true,
&TypeKind::Array(h, _) => self.is_generic(h),
&TypeKind::Instance(h) => self.is_generic(h),
TypeKind::Intrinsic(_) => false,
TypeKind::Primitive(_) => false,
TypeKind::Adt(Adt::Enum(tys)) => tys
.iter()
.any(|(_, ty)| ty.is_some_and(|ty| self.is_generic(ty))),
@ -277,10 +404,10 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
TypeKind::FnSig { args, rety } => {
self.occurs_in(this, *args) || self.occurs_in(this, *rety)
}
TypeKind::Uninferred
TypeKind::Inferred
| TypeKind::Variable
| TypeKind::Adt(Adt::UnitStruct)
| TypeKind::Intrinsic(_)
| TypeKind::Primitive(_)
| TypeKind::Empty
| TypeKind::Never
| TypeKind::Module => false,
@ -296,11 +423,11 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
};
match (a, b) {
(TypeKind::Uninferred, _) => {
(TypeKind::Inferred, _) => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Uninferred) => self.unify(bh, ah),
(_, TypeKind::Inferred) => self.unify(bh, ah),
(TypeKind::Variable, _) => {
self.set_instance(ah, bh);
@ -311,9 +438,26 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
Ok(())
}
(TypeKind::Instance(_), _) => Err(InferenceError::Recursive(ah, bh)),
(_, TypeKind::Variable) | (_, TypeKind::Instance(_)) => self.unify(bh, ah),
(TypeKind::Intrinsic(ia), TypeKind::Intrinsic(ib)) if ia == ib => Ok(()),
(TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(Primitive::Float)) => {
Err(InferenceError::Mismatch(ah, bh))
}
// Primitives have their own set of vars which only unify with primitives.
(TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(i)) if i.is_integer() => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(f)) if f.is_float() => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Variable)
| (_, TypeKind::Instance(_))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Float)) => self.unify(bh, ah),
(TypeKind::Adt(Adt::Enum(ia)), TypeKind::Adt(Adt::Enum(ib)))
if ia.len() == ib.len() =>
{
@ -356,6 +500,9 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
}
(TypeKind::Ref(a), TypeKind::Ref(b)) => self.unify(*a, *b),
(TypeKind::Slice(a), TypeKind::Slice(b)) => self.unify(*a, *b),
// Slice unifies with array
(TypeKind::Array(a, _), TypeKind::Slice(b)) => self.unify(*a, *b),
(TypeKind::Slice(_), TypeKind::Array(_, _)) => self.unify(bh, ah),
(TypeKind::Array(a, sa), TypeKind::Array(b, sb)) if sa == sb => self.unify(*a, *b),
(TypeKind::Tuple(a), TypeKind::Tuple(b)) => {
if a.len() != b.len() {
@ -376,9 +523,8 @@ impl<'table, 'a> InferenceEngine<'table, 'a> {
{
Ok(())
}
(TypeKind::Empty, TypeKind::Empty) => Ok(()),
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(TypeKind::Module, TypeKind::Module) => Ok(()),
(a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)),
}
}

View File

@ -7,16 +7,24 @@ use core::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InferenceError {
AnnotationEval(crate::type_expression::Error),
FieldCount(Handle, usize, usize),
NotFound(Path),
Mismatch(Handle, Handle),
Recursive(Handle, Handle),
}
impl std::error::Error for InferenceError {}
#[rustfmt::skip]
impl fmt::Display for InferenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferenceError::AnnotationEval(error) => write!(f, "{error}"),
InferenceError::FieldCount(name, want, got) => {
write!(f,
"Struct {name} {} fields! Expected {want}, got {got}",
if want < got { "has too many" } else { "is missing" }
)
}
InferenceError::NotFound(p) => write!(f, "Path not visible in scope: {p}"),
InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),

File diff suppressed because it is too large Load Diff

View File

@ -143,9 +143,9 @@ impl<'a> Table<'a> {
entry
}
pub(crate) fn uninferred_type(&mut self) -> Handle {
pub(crate) fn inferred_type(&mut self) -> Handle {
let handle = self.new_entry(self.root, NodeKind::Type);
self.types.insert(handle, TypeKind::Uninferred);
self.types.insert(handle, TypeKind::Inferred);
handle
}

View File

@ -42,7 +42,7 @@ impl TypeExpression for TyKind {
match self {
TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
TyKind::Infer => Ok(table.uninferred_type()),
TyKind::Infer => Ok(table.inferred_type()),
TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node),
TyKind::Slice(s) => s.evaluate(table, node),

View File

@ -11,13 +11,13 @@ mod display;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind {
/// A type that is yet to be inferred!
Uninferred,
/// A type variable, to be filled in later
Inferred,
/// A type variable, to be monomorphized
Variable,
/// An alias for an already-defined type
Instance(Handle),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
Primitive(Primitive),
/// A user-defined aromatic data type
Adt(Adt),
/// A reference to an already-defined type: &T
@ -59,42 +59,64 @@ pub enum Adt {
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Primitive {
I8, I16, I32, I64, I128, Isize, // Signed integers
U8, U16, U32, U64, U128, Usize, // Unsigned integers
F8, F16, F32, F64, F128, Fsize, // Floating point numbers
Integer, Float, // Inferred int and float
Bool, // boolean value
Char, // Unicode codepoint
}
#[rustfmt::skip]
impl Primitive {
/// Checks whether self is an integer
pub fn is_integer(self) -> bool {
matches!(
self,
| Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::Isize
| Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::Usize
| Self::Integer
)
}
/// Checks whether self is a floating point number
pub fn is_float(self) -> bool {
matches!(
self,
| Self::F8 | Self::F16 | Self::F32 | Self::F64 | Self::F128 | Self::Fsize
| Self::Float
)
}
}
// Author's note: the fsize type is a meme
impl FromStr for Intrinsic {
impl FromStr for Primitive {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"i8" => Intrinsic::I8,
"i16" => Intrinsic::I16,
"i32" => Intrinsic::I32,
"i64" => Intrinsic::I64,
"i128" => Intrinsic::I128,
"isize" => Intrinsic::Isize,
"u8" => Intrinsic::U8,
"u16" => Intrinsic::U16,
"u32" => Intrinsic::U32,
"u64" => Intrinsic::U64,
"u128" => Intrinsic::U128,
"usize" => Intrinsic::Usize,
"f8" => Intrinsic::F8,
"f16" => Intrinsic::F16,
"f32" => Intrinsic::F32,
"f64" => Intrinsic::F64,
"f128" => Intrinsic::F128,
"fsize" => Intrinsic::Fsize,
"bool" => Intrinsic::Bool,
"char" => Intrinsic::Char,
"i8" => Primitive::I8,
"i16" => Primitive::I16,
"i32" => Primitive::I32,
"i64" => Primitive::I64,
"i128" => Primitive::I128,
"isize" => Primitive::Isize,
"u8" => Primitive::U8,
"u16" => Primitive::U16,
"u32" => Primitive::U32,
"u64" => Primitive::U64,
"u128" => Primitive::U128,
"usize" => Primitive::Usize,
"f8" => Primitive::F8,
"f16" => Primitive::F16,
"f32" => Primitive::F32,
"f64" => Primitive::F64,
"f128" => Primitive::F128,
"fsize" => Primitive::Fsize,
"bool" => Primitive::Bool,
"char" => Primitive::Char,
_ => Err(())?,
})
}

View File

@ -1,6 +1,6 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Intrinsic, TypeKind};
use super::{Adt, Primitive, TypeKind};
use crate::format_utils::*;
use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write};
@ -8,10 +8,10 @@ use std::fmt::{self, Display, Write};
impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeKind::Uninferred => write!(f, "_"),
TypeKind::Inferred => write!(f, "_"),
TypeKind::Variable => write!(f, "?"),
TypeKind::Instance(def) => write!(f, "alias to #{def}"),
TypeKind::Intrinsic(i) => i.fmt(f),
TypeKind::Primitive(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(def) => write!(f, "&{def}"),
TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
@ -70,29 +70,31 @@ impl Display for Adt {
}
}
impl Display for Intrinsic {
impl Display for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Intrinsic::I8 => f.write_str("i8"),
Intrinsic::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"),
Intrinsic::I128 => f.write_str("i128"),
Intrinsic::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"),
Intrinsic::U128 => f.write_str("u128"),
Intrinsic::Usize => f.write_str("usize"),
Intrinsic::F8 => f.write_str("f8"),
Intrinsic::F16 => f.write_str("f16"),
Intrinsic::F32 => f.write_str("f32"),
Intrinsic::F64 => f.write_str("f64"),
Intrinsic::F128 => f.write_str("f128"),
Intrinsic::Fsize => f.write_str("fsize"),
Intrinsic::Bool => f.write_str("bool"),
Intrinsic::Char => f.write_str("char"),
Primitive::I8 => f.write_str("i8"),
Primitive::I16 => f.write_str("i16"),
Primitive::I32 => f.write_str("i32"),
Primitive::I64 => f.write_str("i64"),
Primitive::I128 => f.write_str("i128"),
Primitive::Isize => f.write_str("isize"),
Primitive::U8 => f.write_str("u8"),
Primitive::U16 => f.write_str("u16"),
Primitive::U32 => f.write_str("u32"),
Primitive::U64 => f.write_str("u64"),
Primitive::U128 => f.write_str("u128"),
Primitive::Usize => f.write_str("usize"),
Primitive::F8 => f.write_str("f8"),
Primitive::F16 => f.write_str("f16"),
Primitive::F32 => f.write_str("f32"),
Primitive::F64 => f.write_str("f64"),
Primitive::F128 => f.write_str("f128"),
Primitive::Fsize => f.write_str("fsize"),
Primitive::Integer => f.write_str("{integer}"),
Primitive::Float => f.write_str("{float}"),
Primitive::Bool => f.write_str("bool"),
Primitive::Char => f.write_str("char"),
}
}
}

View File

@ -1,7 +1,11 @@
//! # The Conlang Standard Library
pub mod preamble {
pub use super::{num::*, str::str};
pub use super::{
num::*,
range::{RangeExc, RangeInc},
str::str,
};
}
pub mod num;

View File

@ -1,51 +1,51 @@
//! The primitive numeric types
#[intrinsic = "bool"]
#[lang = "bool"]
pub type bool;
#[intrinsic = "char"]
#[lang = "char"]
pub type char;
#[intrinsic = "i8"]
#[lang = "i8"]
pub type i8;
#[intrinsic = "i16"]
#[lang = "i16"]
pub type i16;
#[intrinsic = "i32"]
#[lang = "i32"]
pub type i32;
#[intrinsic = "i64"]
#[lang = "i64"]
pub type i64;
#[intrinsic = "i128"]
#[lang = "i128"]
pub type i128;
#[intrinsic = "isize"]
#[lang = "isize"]
pub type isize;
#[intrinsic = "u8"]
#[lang = "u8"]
pub type u8;
#[intrinsic = "u16"]
#[lang = "u16"]
pub type u16;
#[intrinsic = "u32"]
#[lang = "u32"]
pub type u32;
#[intrinsic = "u64"]
#[lang = "u64"]
pub type u64;
#[intrinsic = "u128"]
#[lang = "u128"]
pub type u128;
#[intrinsic = "usize"]
#[lang = "usize"]
pub type usize;
#[intrinsic = "f32"]
#[lang = "f32"]
pub type f32;
#[intrinsic = "f64"]
#[lang = "f64"]
pub type f64;
// Contains implementations for (TODO) overloaded operators on num types

View File

@ -1,9 +1,17 @@
//! Iterable ranges
type T = _;
/// An Exclusive Range `a .. b` iterates from a to b, excluding b
#[intrinsic = "range_exc", T]
struct RangeExc(T, T)
// #[lang = "range_exc", T]
pub struct RangeExc(T, T)
/// An Inclusive Range `a ..= b` iterates from a to b, including b
#[intrinsic = "range_inc", T]
struct RangeInc(T, T)
// #[lang = "range_inc", T]
pub struct RangeInc(T, T)
impl RangeExc {
fn next(this: &RangeInc) -> T {
(*this).0
}
}

View File

@ -1,4 +1,5 @@
//! TODO: give conland a string type
use super::num::u8;
// #[lang = "str"]
type str = [u8];