Initial commit
This commit is contained in:
commit
85b08fe00b
29 changed files with 750 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
66
Cargo.lock
generated
Normal file
66
Cargo.lock
generated
Normal file
|
@ -0,0 +1,66 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shiny"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "shiny-std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fxhash",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shiny-typing"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"shiny-std",
|
||||
]
|
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "shiny"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/typing",
|
||||
"crates/std",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
shiny-typing.path = "crates/typing"
|
||||
shiny-std.path = "crates/std"
|
||||
|
||||
fxhash = "0.2.1"
|
||||
hashbrown = "0.15.0"
|
||||
|
||||
[dependencies]
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Aleksandr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
35
README.md
Normal file
35
README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Shiny
|
||||
|
||||
Expressive systems language. Inspired by Rust, but different, "shiny" is kinda different than "rust".
|
||||
|
||||
# Design
|
||||
|
||||
1. Types and layouts are different things. Type defines
|
||||
**what** data is, **layout** defines how type laid in memory.
|
||||
|
||||
2. Typing discipline is structural, making language easier to use
|
||||
for application programming. Most hugely focus on data and its properties, like
|
||||
"the field xxx" needs to contain date between 2000.01.01 and 2027.01.01 or
|
||||
"the token in request is valid". Clearly, these patterns can be implemented without
|
||||
structural typing by using functions or traits, structural discipline makes it easier to write and
|
||||
easier to introspect, since this allows to make narrower definition
|
||||
of predicate in your domain, while passed function logic can be really arbitrary, it's more general,
|
||||
and you have to deal with that.
|
||||
|
||||
3. Reusing pattern matching for types. In Rust, traits are used to constrain types,
|
||||
in `shiny`, we reuse `pattern matching` to that kind of thing. So, all bounds are encoded
|
||||
as "types fits in this shape" rather than as "the type implements these traits".
|
||||
|
||||
# Some notes
|
||||
|
||||
`shiny` has product and sum types, I call these "binary" types - combination of two types. As an example:
|
||||
`i32 * i32`, alternatively can be written as `(i32, i32)`, also (1) `i32 * i32 * i32 * i32`
|
||||
(2) `(i32, (i32, (i32, i32)))` and `(i32, i32, i32, i32)` are the same types, but (3) `i32 * (i32 * i32) * i32` are NOT, multiplication
|
||||
operator here is left-associative, but all (1, 2, 3) types mentioned here are intechangable. Interchangable means here:
|
||||
If we have constrained type with 1 or 2 or 3 (disambiguation: `or` here means exclusive `or`) **pattern**,
|
||||
all mentioned types (1, 2, 3) will fulfill the constraint, by applying associativity and commutativity.
|
||||
|
||||
Necessary to note, if we have constraint, say, `i32 * str` and type `str * i32 * bool * i32 * str`, what should compiler choose?
|
||||
Compiler simply would prefer part of the type that can be moved to head with less number of expression
|
||||
rewrites. Less strict - we prefer types that are in head, rather than types that are in tail of the type.
|
||||
|
8
crates/std/Cargo.toml
Normal file
8
crates/std/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "shiny-std"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
fxhash.workspace = true
|
||||
hashbrown.workspace = true
|
147
crates/std/src/arena/intern.rs
Normal file
147
crates/std/src/arena/intern.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
//! # Interning arena.
|
||||
//!
|
||||
//! Additionally, if same value was present on the arena, doesn't add new object, returns old.
|
||||
|
||||
use std::hash::{BuildHasherDefault, Hash};
|
||||
|
||||
use hashbrown::hash_map::{HashMap, RawEntryMut};
|
||||
|
||||
use super::{ptr::Ptr, wonly};
|
||||
|
||||
// No raw_entry API on hashset.
|
||||
type Bi<P> = HashMap<Mapping<P>, (), BuildHasherDefault<hasher::NoHash>>;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
struct Mapping<P> {
|
||||
hash: u64,
|
||||
key: P,
|
||||
}
|
||||
|
||||
impl<P> Hash for Mapping<P> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
// [`hasher::NoHash`] implementation ensures that
|
||||
// hasher stores `write_u64` result only once.
|
||||
state.write_u64(self.hash);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Arena<T, P> {
|
||||
objects: wonly::Arena<T, P>,
|
||||
bi: Bi<P>,
|
||||
}
|
||||
|
||||
impl<T, P> Default for Arena<T, P> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
objects: wonly::Arena::new(),
|
||||
bi: Bi::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P> Arena<T, P> {
|
||||
pub fn modify(&mut self, ptr: P, f: impl FnOnce(&mut T)) -> bool
|
||||
where
|
||||
T: Hash + Eq,
|
||||
P: Ptr,
|
||||
{
|
||||
let Some(object) = self.objects.get(ptr.clone()) else {
|
||||
return false;
|
||||
};
|
||||
let hash = fxhash::hash64(object);
|
||||
|
||||
let entry = self
|
||||
.bi
|
||||
.raw_entry_mut()
|
||||
.from_hash(hash, |m| self.objects.get(m.key.clone()) == Some(object));
|
||||
|
||||
// SAFETY: if we got here, the object is in arena.
|
||||
let object = unsafe { self.objects.get_mut(ptr.clone()).unwrap_unchecked() };
|
||||
f(object);
|
||||
|
||||
let hash = fxhash::hash64(object);
|
||||
entry.and_modify(|k, _| *k = Mapping { hash, key: ptr });
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get(&self, ptr: P) -> Option<&T>
|
||||
where
|
||||
P: Ptr,
|
||||
{
|
||||
self.objects.get(ptr)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, object: T) -> P
|
||||
where
|
||||
P: Ptr,
|
||||
T: Hash + Eq,
|
||||
{
|
||||
let hash = fxhash::hash64(&object);
|
||||
match self.bi.raw_entry_mut().from_hash(hash, |m| {
|
||||
self.objects
|
||||
.get(m.key.clone())
|
||||
.map_or(false, |lhs| lhs == &object)
|
||||
}) {
|
||||
RawEntryMut::Occupied(occup) => occup.into_key().key.clone(),
|
||||
RawEntryMut::Vacant(vacant) => {
|
||||
let key = self.objects.insert(object);
|
||||
vacant.insert_hashed_nocheck(
|
||||
hash,
|
||||
Mapping {
|
||||
hash,
|
||||
key: key.clone(),
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
mod hasher;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::arena::ptr::define;
|
||||
|
||||
define! {
|
||||
struct Token;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modification_works() {
|
||||
// Ensure that when modifying the value,
|
||||
// pointer is still valid.
|
||||
|
||||
let mut arena: Arena<u32, Token> = Arena::new();
|
||||
let key100to200 = arena.insert(100);
|
||||
let modified = arena.modify(key100to200, |x| *x = 200);
|
||||
|
||||
assert!(modified);
|
||||
assert_eq!(arena.get(key100to200), Some(&200));
|
||||
|
||||
let key100new = arena.insert(100);
|
||||
assert_ne!(key100to200, key100new);
|
||||
|
||||
assert_eq!(arena.get(key100new), Some(&100));
|
||||
assert_eq!(arena.get(key100to200), Some(&200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_interns() {
|
||||
let mut arena: Arena<u32, Token> = Arena::new();
|
||||
let key100 = arena.insert(100);
|
||||
let key100again = arena.insert(100);
|
||||
|
||||
assert_eq!(key100, key100again);
|
||||
}
|
||||
}
|
22
crates/std/src/arena/intern/hasher.rs
Normal file
22
crates/std/src/arena/intern/hasher.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use std::hash::Hasher;
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct NoHash(Option<u64>);
|
||||
|
||||
impl Hasher for NoHash {
|
||||
fn write_u64(&mut self, i: u64) {
|
||||
if self.0.is_some() {
|
||||
panic!("called write_u64 twice")
|
||||
} else {
|
||||
self.0 = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _: &[u8]) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn finish(&self) -> u64 {
|
||||
self.0.unwrap()
|
||||
}
|
||||
}
|
20
crates/std/src/arena/macros.rs
Normal file
20
crates/std/src/arena/macros.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
/// Combination of pointers.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! _make_comb {
|
||||
($(
|
||||
$item:item
|
||||
)*) => {$(
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
)]
|
||||
$item
|
||||
)*};
|
||||
}
|
8
crates/std/src/arena/mod.rs
Normal file
8
crates/std/src/arena/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub mod intern;
|
||||
pub mod wonly;
|
||||
|
||||
pub mod ptr;
|
||||
|
||||
pub use crate::_make_comb as make_comb;
|
||||
|
||||
mod macros;
|
59
crates/std/src/arena/ptr.rs
Normal file
59
crates/std/src/arena/ptr.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use std::{hash::Hash, num::NonZeroU32};
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! _ptrs {
|
||||
($(
|
||||
$(#[$outer:meta])*
|
||||
$vis:vis struct $name:ident;
|
||||
)*) => {$(
|
||||
$(#[$outer])*
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
)]
|
||||
$vis struct $name(::core::num::NonZeroU32);
|
||||
|
||||
impl $name {
|
||||
/// Create instance of the pointer.
|
||||
pub const fn new(v: ::core::num::NonZeroU32) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
|
||||
pub const fn get(&self) -> ::core::num::NonZeroU32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::convert::From<$name> for ::core::num::NonZeroU32 {
|
||||
fn from(value: $name) -> Self {
|
||||
value.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::convert::From<::core::num::NonZeroU32> for $name {
|
||||
fn from(value: ::core::num::NonZeroU32) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::arena::ptr::Ptr for $name {
|
||||
fn get(&self) -> ::core::num::NonZeroU32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
/// Pointer in arena.
|
||||
pub trait Ptr: Copy + Eq + Into<NonZeroU32> + From<NonZeroU32> + Hash {
|
||||
fn get(&self) -> NonZeroU32;
|
||||
}
|
||||
|
||||
pub use crate::_ptrs as define;
|
60
crates/std/src/arena/wonly.rs
Normal file
60
crates/std/src/arena/wonly.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
//! # Write-only arena, without deletion
|
||||
|
||||
use std::{marker::PhantomData, num::NonZeroU32};
|
||||
|
||||
use super::ptr::Ptr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Arena<T, P> {
|
||||
mem: Vec<T>,
|
||||
_ptr_ty: PhantomData<P>,
|
||||
}
|
||||
|
||||
fn calc_idx(v: NonZeroU32) -> usize {
|
||||
v.get() as usize - 1
|
||||
}
|
||||
|
||||
impl<T, P> Arena<T, P> {
|
||||
pub fn get_mut(&mut self, ptr: P) -> Option<&mut T>
|
||||
where
|
||||
P: Ptr,
|
||||
{
|
||||
self.mem.get_mut(calc_idx(ptr.into()))
|
||||
}
|
||||
|
||||
pub fn get(&self, ptr: P) -> Option<&T>
|
||||
where
|
||||
P: Ptr,
|
||||
{
|
||||
self.mem.get(calc_idx(ptr.into()))
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, obj: T) -> P
|
||||
where
|
||||
P: Ptr,
|
||||
{
|
||||
let idx = self.mem.len();
|
||||
self.mem.push(obj);
|
||||
|
||||
idx.checked_add(1)
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.and_then(NonZeroU32::new)
|
||||
.expect("shit happens")
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P> Default for Arena<T, P> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P> Arena<T, P> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
mem: Vec::new(),
|
||||
_ptr_ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
6
crates/std/src/collections.rs
Normal file
6
crates/std/src/collections.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use std::hash::BuildHasherDefault;
|
||||
|
||||
use fxhash::FxHasher;
|
||||
|
||||
pub type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
pub type HashSet<V> = hashbrown::HashSet<V, BuildHasherDefault<FxHasher>>;
|
3
crates/std/src/lib.rs
Normal file
3
crates/std/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod collections;
|
||||
|
||||
pub mod arena;
|
7
crates/typing/Cargo.toml
Normal file
7
crates/typing/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "shiny-typing"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
shiny-std.workspace = true
|
33
crates/typing/src/bin.rs
Normal file
33
crates/typing/src/bin.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
//! # Binary types
|
||||
|
||||
use shiny_std::arena::make_comb;
|
||||
|
||||
use crate::ptr;
|
||||
|
||||
make_comb! {
|
||||
/// Type with additionally equipped ID.
|
||||
pub struct Field {
|
||||
pub ty: ptr::Ty,
|
||||
pub id: ptr::Id,
|
||||
}
|
||||
|
||||
/// Product type. Pretty much just `lhs_ty * rhs_ty`.
|
||||
/// The following properties must be upheld:
|
||||
///
|
||||
/// 1. lhs * rhs = rhs * lhs <- commutativity
|
||||
/// 2. (a * b) * c = a * (b * c) <- associativity
|
||||
pub struct Prod {
|
||||
pub lhs: ptr::Ty,
|
||||
pub rhs: ptr::Ty,
|
||||
}
|
||||
|
||||
/// Sum type. Pretty much just `lhs_ty + rhs_ty`.
|
||||
/// The following properties must be upheld:
|
||||
///
|
||||
/// 1. lhs + rhs = rhs + rhs <- commutativity
|
||||
/// 2. (a + b) + c = a + (b + c) <- associativity
|
||||
pub struct Sum {
|
||||
pub lhs: ptr::Ty,
|
||||
pub rhs: ptr::Ty,
|
||||
}
|
||||
}
|
1
crates/typing/src/check/ctx.rs
Normal file
1
crates/typing/src/check/ctx.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub struct Ctx {}
|
4
crates/typing/src/check/mod.rs
Normal file
4
crates/typing/src/check/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
//! # Type checking.
|
||||
|
||||
pub mod ctx;
|
||||
pub mod shape;
|
7
crates/typing/src/check/shape.rs
Normal file
7
crates/typing/src/check/shape.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use crate::ptr;
|
||||
|
||||
use super::ctx::Ctx;
|
||||
|
||||
pub fn fits(lhs: ptr::Ty, rhs: ptr::Ty) -> bool {
|
||||
todo!()
|
||||
}
|
15
crates/typing/src/infer/ctx.rs
Normal file
15
crates/typing/src/infer/ctx.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use shiny_std::arena::intern;
|
||||
|
||||
use crate::{bin, int, ptr};
|
||||
|
||||
enum Ty {
|
||||
Prod(bin::Prod),
|
||||
Sum(bin::Sum),
|
||||
Field(bin::Field),
|
||||
Int(int::Int),
|
||||
}
|
||||
|
||||
/// Inference context.
|
||||
pub struct Ctx {
|
||||
tys: intern::Arena<Ty, ptr::Ty>,
|
||||
}
|
3
crates/typing/src/infer/mod.rs
Normal file
3
crates/typing/src/infer/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! # Type inference.
|
||||
|
||||
mod ctx;
|
14
crates/typing/src/int.rs
Normal file
14
crates/typing/src/int.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
//! # Integer types.
|
||||
|
||||
/// Sign of the integer.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Signedness {
|
||||
/// The number has sign.
|
||||
Signed,
|
||||
/// The number has no sign.
|
||||
Unsigned,
|
||||
}
|
||||
|
||||
/// Integer.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Int(pub Signedness);
|
10
crates/typing/src/lib.rs
Normal file
10
crates/typing/src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod bin;
|
||||
pub mod un;
|
||||
|
||||
pub mod int;
|
||||
pub mod pat;
|
||||
|
||||
pub mod ptr;
|
||||
|
||||
pub mod check;
|
||||
pub mod infer;
|
32
crates/typing/src/pat.rs
Normal file
32
crates/typing/src/pat.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use shiny_arena::make_comb;
|
||||
|
||||
use crate::ptr;
|
||||
|
||||
make_comb! {
|
||||
/// Product type pattern.
|
||||
pub struct Prod {
|
||||
pub lhs: ptr::Pat,
|
||||
pub rhs: ptr::Pat,
|
||||
}
|
||||
|
||||
/// Sum type pattern.
|
||||
pub struct Sum {
|
||||
pub lhs: ptr::Pat,
|
||||
pub rhs: ptr::Pat,
|
||||
}
|
||||
|
||||
/// Exact type. Matches only if matched type
|
||||
/// equals to specified.
|
||||
pub struct ExactTy(pub ptr::Ty);
|
||||
|
||||
/// Field type. matches `{ name : ty_pat }`
|
||||
pub struct Field {
|
||||
pub name: ptr::Id,
|
||||
pub ty: ptr::Pat,
|
||||
}
|
||||
|
||||
/// "Value" pattern. Works like [`ExactTy`], but
|
||||
/// additionally checks whether value of that type is equal
|
||||
/// to specified value.
|
||||
pub struct Value(pub ptr::Value);
|
||||
}
|
21
crates/typing/src/ptr.rs
Normal file
21
crates/typing/src/ptr.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//! # Pointers to needed information.
|
||||
//!
|
||||
//! If values are equal, then their pointers are equal.
|
||||
//! This means that there's unambiguous mapping from id to value and
|
||||
//! back.
|
||||
|
||||
use shiny_std::arena::ptr::define;
|
||||
|
||||
define! {
|
||||
/// Pointer to a type.
|
||||
pub struct Ty;
|
||||
|
||||
/// Pointer to ID.
|
||||
pub struct Id;
|
||||
|
||||
/// Pointer to a pattern.
|
||||
pub struct Pat;
|
||||
|
||||
/// Pointer to a *complete* value.
|
||||
pub struct Value;
|
||||
}
|
1
crates/typing/src/un.rs
Normal file
1
crates/typing/src/un.rs
Normal file
|
@ -0,0 +1 @@
|
|||
//! # Unary types
|
82
flake.lock
generated
Normal file
82
flake.lock
generated
Normal file
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1728492678,
|
||||
"narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728786660,
|
||||
"narHash": "sha256-qY+1e0o6oV5ySlErhj/dsWsPLWjrMKzq4QI7a1t9/Ps=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "174a8d9cec9e2c23877a7b887c52b68ef0421d8b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
41
flake.nix
Normal file
41
flake.nix
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
description = "Shiny";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
rust-overlay,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem(system:
|
||||
let
|
||||
overlays = [(import rust-overlay)];
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
|
||||
rust = rec {
|
||||
package = pkgs.rust-bin.nightly.latest.default;
|
||||
devPackage = package.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" ];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
shellHook = ''
|
||||
export PS1="(shiny) $PS1"
|
||||
'';
|
||||
buildInputs = [ rust.devPackage ];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
3
src/main.rs
Normal file
3
src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue