Skip to content

Commit

Permalink
Add construction and deconstruction
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Jul 23, 2024
1 parent 7313c52 commit 8bd10ae
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 38 deletions.
24 changes: 7 additions & 17 deletions crates/rue-compiler/src/compiler/item/type_alias_item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rue_parser::TypeAliasItem;
use rue_typing::{Alias, Type, TypeId};

use crate::{compiler::Compiler, value::Type, ErrorKind, TypeId};
use crate::compiler::Compiler;

impl Compiler<'_> {
/// Define a type for an alias in the current scope, but leave it as unknown for now.
Expand All @@ -22,22 +23,11 @@ impl Compiler<'_> {
.map_or(self.ty.std().unknown, |ty| self.compile_type(ty));

// Set the alias type to the resolved type.
*self.db.ty_mut(alias_type_id) = Type::Alias(type_id);

// A cycle between type aliases has been detected.
// We set it to unknown to prevent stack overflow issues later.
if self.db.is_cyclic(alias_type_id) {
let name = type_alias
.name()
.expect("the name should exist if it's in a cyclic reference");

self.db.error(
ErrorKind::RecursiveTypeAlias(name.to_string()),
name.text_range(),
);

*self.db.ty_mut(alias_type_id) = Type::Unknown;
}
*self.ty.get_mut(alias_type_id) = Type::Alias(Alias {
original_type_id: None,
type_id,
generic_types: Vec::new(),
});

self.type_definition_stack.pop().unwrap();
}
Expand Down
29 changes: 15 additions & 14 deletions crates/rue-typing/src/difference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ use std::collections::HashSet;
use num_bigint::BigInt;
use num_traits::One;

use crate::{bigint_to_bytes, Enum, StandardTypes, Struct, Type, TypeId, TypeSystem, Variant};
use crate::{bigint_to_bytes, Enum, Struct, Type, TypeId, TypeSystem, Variant};

pub(crate) fn difference_type(
types: &mut TypeSystem,
std: &StandardTypes,
lhs: TypeId,
rhs: TypeId,
visited: &mut HashSet<(TypeId, TypeId)>,
) -> TypeId {
let std = types.std();

if !visited.insert((lhs, rhs)) {
return lhs;
}
Expand Down Expand Up @@ -198,8 +199,8 @@ pub(crate) fn difference_type(
let (lhs_first, lhs_rest) = (*lhs_first, *lhs_rest);
let (rhs_first, rhs_rest) = (*rhs_first, *rhs_rest);

let first = difference_type(types, std, lhs_first, rhs_first, visited);
let rest = difference_type(types, std, lhs_rest, rhs_rest, visited);
let first = difference_type(types, lhs_first, rhs_first, visited);
let rest = difference_type(types, lhs_rest, rhs_rest, visited);

if matches!(types.get(first), Type::Never) || matches!(types.get(first), Type::Never) {
std.never
Expand All @@ -216,7 +217,7 @@ pub(crate) fn difference_type(
let mut result = Vec::new();

for item in &items {
let item = difference_type(types, std, *item, rhs, visited);
let item = difference_type(types, *item, rhs, visited);
if matches!(types.get(item), Type::Never) {
continue;
}
Expand All @@ -238,17 +239,17 @@ pub(crate) fn difference_type(
let items = items.clone();
let mut lhs = lhs;
for item in items {
lhs = difference_type(types, std, lhs, item, visited);
lhs = difference_type(types, lhs, item, visited);
}
lhs
}

(Type::Alias(alias), _) => difference_type(types, std, alias.type_id, rhs, visited),
(_, Type::Alias(alias)) => difference_type(types, std, lhs, alias.type_id, visited),
(Type::Alias(alias), _) => difference_type(types, alias.type_id, rhs, visited),
(_, Type::Alias(alias)) => difference_type(types, lhs, alias.type_id, visited),

(Type::Struct(ty), _) => {
let ty = ty.clone();
let type_id = difference_type(types, std, ty.type_id, rhs, visited);
let type_id = difference_type(types, ty.type_id, rhs, visited);

types.alloc(Type::Struct(Struct {
original_type_id: Some(ty.original_type_id.unwrap_or(lhs)),
Expand All @@ -260,7 +261,7 @@ pub(crate) fn difference_type(
}
(_, Type::Struct(ty)) => {
let ty = ty.clone();
let type_id = difference_type(types, std, lhs, ty.type_id, visited);
let type_id = difference_type(types, lhs, ty.type_id, visited);

types.alloc(Type::Struct(Struct {
original_type_id: Some(ty.original_type_id.unwrap_or(rhs)),
Expand All @@ -273,7 +274,7 @@ pub(crate) fn difference_type(

(Type::Enum(ty), _) => {
let ty = ty.clone();
let type_id = difference_type(types, std, ty.type_id, rhs, visited);
let type_id = difference_type(types, ty.type_id, rhs, visited);

types.alloc(Type::Enum(Enum {
original_type_id: Some(ty.original_type_id.unwrap_or(lhs)),
Expand All @@ -284,7 +285,7 @@ pub(crate) fn difference_type(
}
(_, Type::Enum(ty)) => {
let ty = ty.clone();
let type_id = difference_type(types, std, lhs, ty.type_id, visited);
let type_id = difference_type(types, lhs, ty.type_id, visited);

types.alloc(Type::Enum(Enum {
original_type_id: Some(ty.original_type_id.unwrap_or(rhs)),
Expand All @@ -296,7 +297,7 @@ pub(crate) fn difference_type(

(Type::Variant(variant), _) => {
let variant = variant.clone();
let type_id = difference_type(types, std, variant.type_id, rhs, visited);
let type_id = difference_type(types, variant.type_id, rhs, visited);

types.alloc(Type::Variant(Variant {
original_type_id: Some(variant.original_type_id.unwrap_or(lhs)),
Expand All @@ -310,7 +311,7 @@ pub(crate) fn difference_type(
}
(_, Type::Variant(variant)) => {
let variant = variant.clone();
let type_id = difference_type(types, std, lhs, variant.type_id, visited);
let type_id = difference_type(types, lhs, variant.type_id, visited);

types.alloc(Type::Variant(Variant {
original_type_id: Some(variant.original_type_id.unwrap_or(rhs)),
Expand Down
192 changes: 191 additions & 1 deletion crates/rue-typing/src/semantic_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;
use indexmap::IndexSet;
use num_bigint::BigInt;

use crate::TypeId;
use crate::{Comparison, Type, TypeId, TypeSystem};

/// The kind of ending that a list has.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -86,3 +86,193 @@ pub struct Variant {
/// The discriminant value.
pub discriminant: BigInt,
}

/// Constructs a structural type consisting of the items in a list.
pub fn construct_items(
db: &mut TypeSystem,
items: impl DoubleEndedIterator<Item = TypeId>,
rest: Rest,
) -> TypeId {
let mut result = db.std().nil;
for (i, item) in items.rev().enumerate() {
if i == 0 {
match rest {
Rest::Spread => {
result = item;
continue;
}
Rest::Optional => {
result = db.alloc(Type::Pair(item, result));
result = db.alloc(Type::Union(vec![result, db.std().nil]));
continue;
}
Rest::Nil => {}
}
}
result = db.alloc(Type::Pair(item, result));
}
result
}

/// Deconstructs a structural type into a list of items and a rest value.
pub fn deconstruct_items(
db: &mut TypeSystem,
type_id: TypeId,
length: usize,
rest: Rest,
) -> Option<Vec<TypeId>> {
let mut items = Vec::with_capacity(length);
let mut current = type_id;

for i in (0..length).rev() {
if i == 0 {
match rest {
Rest::Spread => {
items.push(current);
break;
}
Rest::Optional => {
if db.compare(db.std().nil, current) > Comparison::Assignable {
return None;
}

let non_nil = db.difference(current, db.std().nil);
let (first, rest) = db.get_pair(non_nil)?;

if db.compare(rest, db.std().nil) > Comparison::Equal {
return None;
}

items.push(first);
break;
}
Rest::Nil => {
let (first, rest) = db.get_pair(current)?;
items.push(first);
if db.compare(rest, db.std().nil) > Comparison::Equal {
return None;
}
break;
}
}
}
let (first, rest) = db.get_pair(current)?;
items.push(first);
current = rest;
}

Some(items)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_construct_int_nil() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Nil);
let items = deconstruct_items(&mut db, type_id, 1, Rest::Nil);
assert_eq!(items, Some(vec![std.int]));
}

#[test]
fn test_construct_int() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Spread);
let items = deconstruct_items(&mut db, type_id, 1, Rest::Spread);
assert_eq!(items, Some(vec![std.int]));
}

#[test]
fn test_construct_int_optional() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int].into_iter(), Rest::Optional);
let items = deconstruct_items(&mut db, type_id, 1, Rest::Optional);
assert_eq!(items, Some(vec![std.int]));
}

#[test]
fn test_construct_empty_nil() {
let mut db = TypeSystem::new();
let type_id = construct_items(&mut db, [].into_iter(), Rest::Nil);
let items = deconstruct_items(&mut db, type_id, 0, Rest::Nil);
assert_eq!(items, Some(vec![]));
}

#[test]
fn test_construct_empty() {
let mut db = TypeSystem::new();
let type_id = construct_items(&mut db, [].into_iter(), Rest::Spread);
let items = deconstruct_items(&mut db, type_id, 0, Rest::Spread);
assert_eq!(items, Some(vec![]));
}

#[test]
fn test_construct_empty_optional() {
let mut db = TypeSystem::new();
let type_id = construct_items(&mut db, [].into_iter(), Rest::Optional);
let items = deconstruct_items(&mut db, type_id, 0, Rest::Optional);
assert_eq!(items, Some(vec![]));
}

#[test]
fn test_construct_int_int_nil() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Nil);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Nil);
assert_eq!(items, Some(vec![std.int, std.int]));
}

#[test]
fn test_construct_int_int() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Spread);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Spread);
assert_eq!(items, Some(vec![std.int, std.int]));
}

#[test]
fn test_construct_int_int_optional() {
let mut db = TypeSystem::new();
let std = db.std();
let type_id = construct_items(&mut db, [std.int, std.int].into_iter(), Rest::Optional);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Optional);
assert_eq!(items, Some(vec![std.int, std.int]));
}

#[test]
fn test_construct_bytes32_pair_nil() {
let mut db = TypeSystem::new();
let std = db.std();
let pair = db.alloc(Type::Pair(std.bytes32, std.nil));
let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Nil);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Nil);
assert_eq!(items, Some(vec![std.bytes32, pair]));
}

#[test]
fn test_construct_bytes32_pair() {
let mut db = TypeSystem::new();
let std = db.std();
let pair = db.alloc(Type::Pair(std.bytes32, std.nil));
let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Spread);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Spread);
assert_eq!(items, Some(vec![std.bytes32, pair]));
}

#[test]
fn test_construct_bytes32_pair_optional() {
let mut db = TypeSystem::new();
let std = db.std();
let pair = db.alloc(Type::Pair(std.bytes32, std.nil));
let type_id = construct_items(&mut db, [std.bytes32, pair].into_iter(), Rest::Optional);
let items = deconstruct_items(&mut db, type_id, 2, Rest::Optional);
assert_eq!(items, Some(vec![std.bytes32, pair]));
}
}
12 changes: 6 additions & 6 deletions crates/rue-typing/src/type_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ impl TypeSystem {
}
}

pub fn pair_first(&self, type_id: TypeId) -> Option<TypeId> {
pub fn get_pair(&self, type_id: TypeId) -> Option<(TypeId, TypeId)> {
match self.get(type_id) {
Type::Pair(first, _) => Some(*first),
Type::Pair(first, rest) => Some((*first, *rest)),
_ => None,
}
}

pub fn pair_rest(&self, type_id: TypeId) -> Option<TypeId> {
pub fn get_union(&self, type_id: TypeId) -> Option<&[TypeId]> {
match self.get(type_id) {
Type::Pair(_, rest) => Some(*rest),
Type::Union(types) => Some(types),
_ => None,
}
}
Expand Down Expand Up @@ -178,8 +178,8 @@ impl TypeSystem {
check_type(self, lhs, rhs, &mut HashSet::new()).map(simplify_check)
}

pub fn difference(&mut self, std: &StandardTypes, lhs: TypeId, rhs: TypeId) -> TypeId {
difference_type(self, std, lhs, rhs, &mut HashSet::new())
pub fn difference(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId {
difference_type(self, lhs, rhs, &mut HashSet::new())
}

pub fn replace(
Expand Down

0 comments on commit 8bd10ae

Please sign in to comment.