92 Commits

Author SHA1 Message Date
3cb85c7f42 toki: pona 2025-02-19 04:04:40 -06:00
3b14186b70 sample-code: Add match_test.cl
Demonstrates pattern matching
2025-02-18 21:53:32 -06:00
a6ad20911d builtins: Add temp builtin for dumping the global string pool 2025-02-18 21:49:59 -06:00
01cf9d93e2 cl-repl: Usability improvements
- Don't print empty
- Don't needlessly append newline to cleared screen
2025-02-18 21:48:36 -06:00
edabbe1655 cl-interpret: process use items and imports in the interpreter 2025-02-18 21:44:52 -06:00
af9c293907 cl-interpret, cl-repl:
Move IO builtins into the CLI, so get_line can use repline keybinds.
2025-02-06 21:35:17 -06:00
0e3ba342c4 cl-interpret: make the error type smaller (at the cost of a heap allocation) 2025-01-31 03:35:35 -06:00
d95d35268e cl-interpret: Builtin refactor
- Everything is different now
  - Builtins are now built on top of Rust functions, so they can be recursive!
  - TODO: allow macro-defined builtins to call each other?
- The builtins! macro is a lot nicer to work with
  - No redundant return value
  - Maps the result over Into::into, allowing for type inference!
  - Uses explicit pattern syntax instead of weird binding, where possible
  - Does not #[allow(unused)], so you'll get unused variable warnings now!
2025-01-31 03:34:45 -06:00
0c2b0002ce conlang: Docs! 2025-01-29 05:07:08 -06:00
3534be5fbc conlang: bump version number for pattern matching and destructured bindings 2025-01-29 04:16:53 -06:00
026681787a cl-interpret: Add a new pretty-debug-printer builtin 2025-01-29 04:16:28 -06:00
80e1219808 cl-interpret: Tests for new pattern matching behavior
TODO: Expand control flow tests
2025-01-29 04:15:57 -06:00
6ee9bbd72e conlang: PATTERN MATCHING AND DESTRUCTURED BINDINGS WOOOOO
- Integrate the Match and Pattern nodes into the AST
  - TODO: `let x: T` is ambiguous with `let x: {}`. Currently the latter takes precedence in the parser.

- Implement pattern matching through unification in the interpreter.
  - It's not fast, but it works!

- Refactor destructuring assignments to use the new pattern functionality
2025-01-29 04:15:33 -06:00
6e94b702c9 cl-ast: Add pattern and match nodes, and associated behaviors 2025-01-29 04:05:11 -06:00
d21683ad61 conlang: Add Quote expression as a hack for testing
Possibly removed later, or replaced with something that turns Conlang AST nodes into Conlang data structures.
2025-01-29 03:56:19 -06:00
518fbe74a1 cl-ast: Fix AddrOf misbehavior 2025-01-29 03:31:24 -06:00
bc955c6409 conlang: Bump version number :D 2025-01-28 06:56:48 -06:00
678c0f952c sample-code: Add demo for get_line() 2025-01-28 06:56:30 -06:00
86c4da0689 cl-repl: Don't print ConValue::Empty return values 2025-01-28 06:55:03 -06:00
5db77db6b8 cl-interpret: Use dyn dispatch for iterators.
- This is a hack because the language has the syntax but no concept of iterators
2025-01-28 06:25:04 -06:00
145a24c5ff cl-interpret: Assignment 2.0, now with more destructuring!
TODO: Destructuring `let`, destructuring patterns in function args..?
2025-01-28 06:23:37 -06:00
485afb7843 cl-interpret: Make ConValues act like value types
(Aside from the fact that they're smothered in heap allocations)
2025-01-28 06:20:10 -06:00
01871bf455 cl-interpret: get_line builtin! 2025-01-28 06:14:05 -06:00
fd361f2bea cl-interpret: Upvars 2.0
- Only captures locals
2025-01-28 06:13:38 -06:00
0eef6b910c cl-interpret/collect_upvars: elide lifetime 2025-01-23 18:53:07 -06:00
c50940a44c cl-interpret: Make an attempt at closures
(It kinda sucks, but it emulates closures half decently)
2025-01-23 16:23:42 -06:00
3cda3d83d9 typeck: Replace unsafe static mut with tree interning,
I used tree interning here, because the interner already contains the necessary locks to make it Sync, and I was lazy. Be my guest if you want to do something else.

The computational overhead of interning the ASTs here is negligible, since this is all CLI code anyway.
2025-01-16 21:16:46 -06:00
e5a51ba6c2 cl-structures: add to_ref associated function on Interned type, for non-borrowing conversion 2025-01-16 21:01:28 -06:00
883fd31d38 conlang: Elide lifetimes (fixes clippy lint) 2025-01-16 20:57:33 -06:00
d71276b477 cl-structures: Update error type in unstable get_many_mut feature for IndexMap 2025-01-16 20:32:09 -06:00
d8e32ee263 cl-arena: promote to its own independent repository 2025-01-16 20:30:23 -06:00
e419c23769 repline: fix operator precedence bug, clippy lints 2025-01-16 20:29:43 -06:00
1bd9c021dd sample-code: Fix typo in sqrt.cl 2024-11-21 21:57:10 -06:00
df68d6f2e6 cl-interpret/tests: Fix broken test 2024-09-19 14:29:21 -05:00
ae11d87d68 Merge pull request 'Basic floating point support (WIP)' (#18) from floats into main
Reviewed-on: #18
2024-09-19 19:27:51 +00:00
96be5aba6c repline: Add a code sample demonstrating the use of prebaked::read_and 2024-09-19 14:08:50 -05:00
b9f4994930 sample-code: update sqrt.cl to use new float syntax 2024-09-19 14:02:58 -05:00
f4fe07a08b cl-lexer: Hack around ambiguity between 1.0 and 1..0
This requires more than one token lookahead, but is already part of a hack itself, so... /shrug
2024-09-19 14:02:02 -05:00
94be5d787f cl-ast: always pretty-print decimal for floats 2024-09-19 14:00:22 -05:00
5deb585054 cl-ast: Add float support
- Smuggle floats as integers to maintain `eq`
- This is bad, but not terrible for spec-compliant floats. Might have issues with NaN.

cl_parser: Smuggle floats

cl_interpret: unpack smuggled floats in float literal node
2024-09-19 13:20:19 -05:00
56e71d6782 cl-lexer: Add a hacky workaround for float support.
It's disgusting, but better than nothing!
2024-09-19 13:16:27 -05:00
c62df3d8b3 sample-code: Add square root demo 2024-09-18 01:53:04 -05:00
fad28beb05 interpreter: Add float machinery
- operators
- type casting
2024-09-18 01:02:09 -05:00
0f8b0824ac cl-parser: Fix precedence of comparison operators 2024-09-18 00:57:44 -05:00
99a00875a8 cl-intern: Derive Default for StringInterner and TypedInterner 2024-08-24 18:18:22 -05:00
8675f91aca cl-ast: Remove tail from let (it caused more problems that it could've solved) 2024-07-31 03:19:20 -05:00
de63a8c123 cl-parser: Outline precedence parser 2024-07-31 02:55:01 -05:00
533436afc1 cl-parser: Move precedence parser into its own module 2024-07-31 02:48:39 -05:00
1eb0516baf cl-parser: Rearrange to match cl-ast
Also reorder `Let` in the AST
2024-07-31 02:35:41 -05:00
97808fd855 cl-parser: Transliterate to a trait-based parsing implementation
Bump version number.
2024-07-31 01:39:00 -05:00
388a69948e Revert "cl-ast: Unify break, return, and unary expressions"
This reverts commit adb0fd229c.
2024-07-30 22:31:39 -05:00
5e7ba6de24 cl-ast: Improve formatting of blocks and groups 2024-07-30 20:40:22 -05:00
adb0fd229c cl-ast: Unify break, return, and unary expressions 2024-07-30 20:16:07 -05:00
0e545077c6 cl-ast: Remove "Continue" struct 2024-07-30 19:42:28 -05:00
b64cc232f9 cl-ast: Move loop expression into unary exprs (with lowest priority) 2024-07-30 18:21:25 -05:00
b0341f06fd cl-ast: Move let into Expr 2024-07-30 18:02:09 -05:00
a3e383b53f cl-token: Flatten TokenKind into a single enum (wow!) 2024-07-30 16:47:09 -05:00
1b217b2e75 typeck: Add a query for all strings 2024-07-29 15:55:53 -05:00
5662bd8524 cl-structures: (ab)use the Display trait to print a numbered, sorted list of interned strings. 2024-07-29 15:55:12 -05:00
28f9048087 cl-typeck: Fix infer.rs doctests 2024-07-29 15:42:35 -05:00
b17164b68b cl-interpret: Write an example for driving the interpreter 2024-07-29 15:42:05 -05:00
ecebefe218 cl-interpret: Knock those modules free! 2024-07-27 22:47:46 -05:00
fc374e0108 ascii.cl: TODO: throw out the interpreter (EVIL) 2024-07-27 20:00:22 -05:00
4295982876 ascii.cl: Cleanup on aisle "bitwise" 2024-07-27 19:59:35 -05:00
729155d3a4 ascii.cl: Fix type annotations (though they're not yet evaluated in the interpreter) 2024-07-27 19:38:41 -05:00
8c0ae02a71 sample-code/ascii.cl: Make it cooler
- Compute char value of digit
- Substitute C0 control codes for Unicode C0 Control Pictures
- Extend through Unicode Latin-1 Supplement
- Blank out C1 control code range
2024-07-27 19:34:37 -05:00
7f7836877e sample-code: Add shebang comments to samples with a main() function 2024-07-27 18:56:36 -05:00
b2733aa171 cl-interpret/builtin: Add len builtin as a quick hack to write more interesting programs.
This is temporary, I just want a way to get the length of a thing atm.
2024-07-27 18:43:03 -05:00
a233bb18bc cl-lexer: Record the contents of comments 2024-07-27 18:41:50 -05:00
e06a27a5b1 cl-lexer: Treat #!/ | #!\ as a comment 2024-07-27 18:41:18 -05:00
3f5c5480ae cl-interpret: [NOT FINAL] Add unicode-aware O(n) string indexing 2024-07-27 18:04:39 -05:00
53cf71608a sample-code/hex.cl: Fix casting TODO, add to_string_radix function 2024-07-27 17:46:27 -05:00
883c2677d9 cl-parser: Index is NOT a low precedence operator!!! 2024-07-27 17:37:29 -05:00
7d98ef87d5 sample-code: proper type annotations on HEX_LUT, add FIXME for min and max 2024-07-26 06:22:29 -05:00
a188c5b65e hex.cl: make the lut square 2024-07-26 06:17:00 -05:00
872818fe7c sample-code/fib.cl: rename fib-iterative -> fibit (easier to type) 2024-07-26 06:13:59 -05:00
3aef055739 sample-code/ascii: Use as casting to print the entire printable ASCII range 2024-07-26 06:10:59 -05:00
38a5d31b08 cl-ast: Escape string and char literals when pretty-printing 2024-07-26 05:51:20 -05:00
e43847bbd4 conlang: Introduce as casting
Arbitrary primitive type conversion

Currently implemented as jankily as possible in the interpreter, but it works alright™️
2024-07-26 05:26:08 -05:00
a8b8a91c79 sample-code: print->println to match interpreter behavior 2024-07-26 05:13:52 -05:00
695c812bf5 cl-repl: increase jank: first positional arg is main file, remainder are imports 2024-07-26 05:13:06 -05:00
524c84be9e cl_typeck: Add new primitive types (including joking-point numbers) 2024-07-26 03:24:34 -05:00
4096442f75 cl-typeck: Turn ref into a linked list.
This should be fine, since the only thing you can do with a ref is dereference it.
2024-07-26 02:14:41 -05:00
03a4e76292 cl-typeck: rustfmt implement.rs 2024-07-26 00:15:00 -05:00
46a1639990 sample-code: Have fun with random number generators 2024-07-25 23:59:41 -05:00
5ea8039a8a typeck.rs: Update for new stdlib layout; don't hardcode the root stdlib module. 2024-07-25 07:09:12 -05:00
479efbad73 typeck.rs: Add file-loading mode 2024-07-25 07:08:07 -05:00
a462dd2be3 stdlib: Use Conlang module layout 2024-07-25 07:05:57 -05:00
4d6b94b570 conlang: Bump version to v0.0.6
- Major milestone: cl-typeck doesn't suck as much!
2024-07-25 05:56:05 -05:00
fe2b816f27 cl-typeck: Crate-spanning refactor part 2
- Removed all unreferenced files
- Reimplemented missing/nonfunctional behavior
- Added module documentation for most things
  - TODO: item-level docs on Entry(Mut)
- Reparented the stages of Table population into the `stage` module.
  - TODO: rewrite type inference to use only the tools provided by Table.
2024-07-25 05:55:11 -05:00
e19127facc cl-typeck: Crate-spanning refactor
TODO: remove all unreferenced files
TODO: Finish resolving statically known types of values
TODO: Type inference
2024-07-24 18:22:42 -05:00
b7ad285a11 cl-typeck: give Handle accessors for useful attributes 2024-07-24 14:29:27 -05:00
81 changed files with 6014 additions and 5016 deletions

View File

@@ -8,14 +8,13 @@ members = [
"compiler/cl-ast", "compiler/cl-ast",
"compiler/cl-parser", "compiler/cl-parser",
"compiler/cl-lexer", "compiler/cl-lexer",
"compiler/cl-arena",
"repline", "repline",
] ]
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
repository = "https://git.soft.fish/j/Conlang" repository = "https://git.soft.fish/j/Conlang"
version = "0.0.5" version = "0.0.9"
authors = ["John Breaux <j@soft.fish>"] authors = ["John Breaux <j@soft.fish>"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"

View File

@@ -1,10 +0,0 @@
[package]
name = "cl-arena"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]

View File

@@ -1,42 +0,0 @@
use super::DroplessArena;
extern crate std;
use core::alloc::Layout;
use std::{prelude::rust_2021::*, vec};
#[test]
fn alloc_raw() {
let arena = DroplessArena::new();
let bytes = arena.alloc_raw(Layout::for_value(&0u128));
let byte2 = arena.alloc_raw(Layout::for_value(&0u128));
assert_ne!(bytes, byte2);
}
#[test]
fn alloc() {
let arena = DroplessArena::new();
let mut allocations = vec![];
for i in 0..0x400 {
allocations.push(arena.alloc(i));
}
}
#[test]
fn alloc_strings() {
const KW: &[&str] = &["pub", "mut", "fn", "mod", "conlang", "sidon", "🦈"];
let arena = DroplessArena::new();
let mut allocations = vec![];
for _ in 0..100 {
for kw in KW {
allocations.push(arena.alloc_str(kw));
}
}
}
#[test]
#[should_panic]
fn alloc_zsts() {
struct Zst;
let arena = DroplessArena::new();
arena.alloc(Zst);
}

View File

@@ -1,396 +0,0 @@
//! Typed and dropless arena allocation, paraphrased from [the Rust Compiler's `rustc_arena`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_arena/src/lib.rs). See [LICENSE][1].
//!
//! An Arena Allocator is a type of allocator which provides stable locations for allocations within
//! itself for the entire duration of its lifetime.
//!
//! [1]: https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT
#![feature(dropck_eyepatch, new_uninit, strict_provenance)]
#![no_std]
extern crate alloc;
pub(crate) mod constants {
//! Size constants for arena chunk growth
pub(crate) const MIN_CHUNK: usize = 4096;
pub(crate) const MAX_CHUNK: usize = 2 * 1024 * 1024;
}
mod chunk {
//! An [ArenaChunk] contains a block of raw memory for use in arena allocators.
use alloc::boxed::Box;
use core::{
mem::{self, MaybeUninit},
ptr::{self, NonNull},
};
pub struct ArenaChunk<T> {
pub(crate) mem: NonNull<[MaybeUninit<T>]>,
pub(crate) filled: usize,
}
impl<T: Sized> ArenaChunk<T> {
pub fn new(cap: usize) -> Self {
let slice = Box::new_uninit_slice(cap);
Self { mem: NonNull::from(Box::leak(slice)), filled: 0 }
}
/// Drops all elements inside self, and resets the filled count to 0
///
/// # Safety
///
/// The caller must ensure that `self.filled` elements of self are currently initialized
pub unsafe fn drop_elements(&mut self) {
if mem::needs_drop::<T>() {
// Safety: the caller has ensured that `filled` elements are initialized
unsafe {
let slice = self.mem.as_mut();
for t in slice[..self.filled].iter_mut() {
t.assume_init_drop();
}
}
self.filled = 0;
}
}
/// Gets a pointer to the start of the arena
pub fn start(&mut self) -> *mut T {
self.mem.as_ptr() as _
}
/// Gets a pointer to the end of the arena
pub fn end(&mut self) -> *mut T {
if mem::size_of::<T>() == 0 {
ptr::without_provenance_mut(usize::MAX) // pointers to ZSTs must be unique
} else {
unsafe { self.start().add(self.mem.len()) }
}
}
}
impl<T> Drop for ArenaChunk<T> {
fn drop(&mut self) {
let _ = unsafe { Box::from_raw(self.mem.as_ptr()) };
}
}
}
pub mod typed_arena {
//! A [TypedArena] can hold many instances of a single type, and will properly [Drop] them.
#![allow(clippy::mut_from_ref)]
use crate::{chunk::ArenaChunk, constants::*};
use alloc::vec::Vec;
use core::{
cell::{Cell, RefCell},
marker::PhantomData,
mem, ptr, slice,
};
/// A [TypedArena] can hold many instances of a single type, and will properly [Drop] them when
/// it falls out of scope.
pub struct TypedArena<'arena, T> {
_lives: PhantomData<&'arena T>,
_drops: PhantomData<T>,
chunks: RefCell<Vec<ArenaChunk<T>>>,
head: Cell<*mut T>,
tail: Cell<*mut T>,
}
impl<'arena, T> Default for TypedArena<'arena, T> {
fn default() -> Self {
Self::new()
}
}
impl<'arena, T> TypedArena<'arena, T> {
pub const fn new() -> Self {
Self {
_lives: PhantomData,
_drops: PhantomData,
chunks: RefCell::new(Vec::new()),
head: Cell::new(ptr::null_mut()),
tail: Cell::new(ptr::null_mut()),
}
}
pub fn alloc(&'arena self, value: T) -> &'arena mut T {
if self.head == self.tail {
self.grow(1);
}
let out = if mem::size_of::<T>() == 0 {
self.head
.set(ptr::without_provenance_mut(self.head.get().addr() + 1));
ptr::NonNull::<T>::dangling().as_ptr()
} else {
let out = self.head.get();
self.head.set(unsafe { out.add(1) });
out
};
unsafe {
ptr::write(out, value);
&mut *out
}
}
fn can_allocate(&self, len: usize) -> bool {
len <= unsafe { self.tail.get().offset_from(self.head.get()) as usize }
}
/// # Panics
/// Panics if size_of::<T> == 0 || len == 0
#[inline]
fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(mem::size_of::<T>() != 0);
assert!(len != 0);
if !self.can_allocate(len) {
self.grow(len)
}
let out = self.head.get();
unsafe { self.head.set(out.add(len)) };
out
}
pub fn alloc_from_iter<I>(&'arena self, iter: I) -> &'arena mut [T]
where I: IntoIterator<Item = T> {
// Collect them all into a buffer so they're allocated contiguously
let mut buf = iter.into_iter().collect::<Vec<_>>();
if buf.is_empty() {
return &mut [];
}
let len = buf.len();
// If T is a ZST, calling alloc_raw_slice will panic
let slice = if mem::size_of::<T>() == 0 {
self.head
.set(ptr::without_provenance_mut(self.head.get().addr() + len));
ptr::NonNull::dangling().as_ptr()
} else {
self.alloc_raw_slice(len)
};
unsafe {
buf.as_ptr().copy_to_nonoverlapping(slice, len);
buf.set_len(0);
slice::from_raw_parts_mut(slice, len)
}
}
#[cold]
#[inline(never)]
fn grow(&self, len: usize) {
let size = mem::size_of::<T>().max(1);
let mut chunks = self.chunks.borrow_mut();
let capacity = if let Some(last) = chunks.last_mut() {
last.filled = self.get_filled_of_chunk(last);
last.mem.len().min(MAX_CHUNK / size) * 2
} else {
MIN_CHUNK / size
}
.max(len);
let mut chunk = ArenaChunk::<T>::new(capacity);
self.head.set(chunk.start());
self.tail.set(chunk.end());
chunks.push(chunk);
}
fn get_filled_of_chunk(&self, chunk: &mut ArenaChunk<T>) -> usize {
let Self { head: tail, .. } = self;
let head = chunk.start();
if mem::size_of::<T>() == 0 {
tail.get().addr() - head.addr()
} else {
unsafe { tail.get().offset_from(head) as usize }
}
}
}
unsafe impl<'arena, T: Send> Send for TypedArena<'arena, T> {}
unsafe impl<'arena, #[may_dangle] T> Drop for TypedArena<'arena, T> {
fn drop(&mut self) {
let mut chunks = self.chunks.borrow_mut();
if let Some(last) = chunks.last_mut() {
last.filled = self.get_filled_of_chunk(last);
self.tail.set(self.head.get());
}
for chunk in chunks.iter_mut() {
unsafe { chunk.drop_elements() }
}
}
}
#[cfg(test)]
mod tests;
}
pub mod dropless_arena {
//! A [DroplessArena] can hold *any* combination of types as long as they don't implement
//! [Drop].
use crate::{chunk::ArenaChunk, constants::*};
use alloc::vec::Vec;
use core::{
alloc::Layout,
cell::{Cell, RefCell},
marker::PhantomData,
mem, ptr, slice,
};
pub struct DroplessArena<'arena> {
_lives: PhantomData<&'arena u8>,
chunks: RefCell<Vec<ArenaChunk<u8>>>,
head: Cell<*mut u8>,
tail: Cell<*mut u8>,
}
impl Default for DroplessArena<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'arena> DroplessArena<'arena> {
pub const fn new() -> Self {
Self {
_lives: PhantomData,
chunks: RefCell::new(Vec::new()),
head: Cell::new(ptr::null_mut()),
tail: Cell::new(ptr::null_mut()),
}
}
/// Allocates a `T` in the [DroplessArena], and returns a mutable reference to it.
///
/// # Panics
/// - Panics if T implements [Drop]
/// - Panics if T is zero-sized
#[allow(clippy::mut_from_ref)]
pub fn alloc<T>(&'arena self, value: T) -> &'arena mut T {
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
let out = self.alloc_raw(Layout::new::<T>()) as *mut T;
unsafe {
ptr::write(out, value);
&mut *out
}
}
/// Allocates a slice of `T`s`, copied from the given slice, returning a mutable reference
/// to it.
///
/// # Panics
/// - Panics if T implements [Drop]
/// - Panics if T is zero-sized
/// - Panics if the slice is empty
#[allow(clippy::mut_from_ref)]
pub fn alloc_slice<T: Copy>(&'arena self, slice: &[T]) -> &'arena mut [T] {
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
assert!(!slice.is_empty());
let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
unsafe {
mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len());
slice::from_raw_parts_mut(mem, slice.len())
}
}
/// Allocates a copy of the given [`&str`](str), returning a reference to the allocation.
///
/// # Panics
/// Panics if the string is empty.
pub fn alloc_str(&'arena self, string: &str) -> &'arena str {
let slice = self.alloc_slice(string.as_bytes());
// Safety: This is a clone of the input string, which was valid
unsafe { core::str::from_utf8_unchecked(slice) }
}
/// Allocates some [bytes](u8) based on the given [Layout].
///
/// # Panics
/// Panics if the provided [Layout] has size 0
pub fn alloc_raw(&'arena self, layout: Layout) -> *mut u8 {
/// Rounds the given size (or pointer value) *up* to the given alignment
fn align_up(size: usize, align: usize) -> usize {
(size + align - 1) & !(align - 1)
}
/// Rounds the given size (or pointer value) *down* to the given alignment
fn align_down(size: usize, align: usize) -> usize {
size & !(align - 1)
}
assert!(layout.size() != 0);
loop {
let Self { head, tail, .. } = self;
let start = head.get().addr();
let end = tail.get().addr();
let align = 8.max(layout.align());
let bytes = align_up(layout.size(), align);
if let Some(end) = end.checked_sub(bytes) {
let end = align_down(end, layout.align());
if start <= end {
tail.set(tail.get().with_addr(end));
return tail.get();
}
}
self.grow(layout.size());
}
}
/// Grows the allocator, doubling the chunk size until it reaches [MAX_CHUNK].
#[cold]
#[inline(never)]
fn grow(&self, len: usize) {
let mut chunks = self.chunks.borrow_mut();
let capacity = if let Some(last) = chunks.last_mut() {
last.mem.len().min(MAX_CHUNK / 2) * 2
} else {
MIN_CHUNK
}
.max(len);
let mut chunk = ArenaChunk::<u8>::new(capacity);
self.head.set(chunk.start());
self.tail.set(chunk.end());
chunks.push(chunk);
}
/// Checks whether the given slice is allocated in this arena
pub fn contains_slice<T>(&self, slice: &[T]) -> bool {
let ptr = slice.as_ptr().cast::<u8>().cast_mut();
for chunk in self.chunks.borrow_mut().iter_mut() {
if chunk.start() <= ptr && ptr <= chunk.end() {
return true;
}
}
false
}
}
unsafe impl<'arena> Send for DroplessArena<'arena> {}
#[cfg(test)]
mod tests;
}

View File

@@ -1,61 +0,0 @@
use super::TypedArena;
extern crate std;
use std::{prelude::rust_2021::*, print, vec};
#[test]
fn pushing_to_arena() {
let arena = TypedArena::new();
let foo = arena.alloc("foo");
let bar = arena.alloc("bar");
let baz = arena.alloc("baz");
assert_eq!("foo", *foo);
assert_eq!("bar", *bar);
assert_eq!("baz", *baz);
}
#[test]
fn pushing_vecs_to_arena() {
let arena = TypedArena::new();
let foo = arena.alloc(vec!["foo"]);
let bar = arena.alloc(vec!["bar"]);
let baz = arena.alloc(vec!["baz"]);
assert_eq!("foo", foo[0]);
assert_eq!("bar", bar[0]);
assert_eq!("baz", baz[0]);
}
#[test]
fn pushing_zsts() {
struct ZeroSized;
impl Drop for ZeroSized {
fn drop(&mut self) {
print!("")
}
}
let arena = TypedArena::new();
for _ in 0..0x100 {
arena.alloc(ZeroSized);
}
}
#[test]
fn pushing_nodrop_zsts() {
struct ZeroSized;
let arena = TypedArena::new();
for _ in 0..0x1000 {
arena.alloc(ZeroSized);
}
}
#[test]
fn resize() {
let arena = TypedArena::new();
for _ in 0..0x780 {
arena.alloc(0u128);
}
}

View File

@@ -8,6 +8,7 @@
//! - [Assign], [Modify], [Binary], and [Unary] expressions //! - [Assign], [Modify], [Binary], and [Unary] expressions
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators //! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers //! - [Ty] and [TyKind]: Type qualifiers
//! - [Pattern]: Pattern matching operators
//! - [Path]: Path expressions //! - [Path]: Path expressions
use cl_structures::{intern::interned::Interned, span::*}; use cl_structures::{intern::interned::Interned, span::*};
@@ -36,6 +37,7 @@ pub enum Literal {
Bool(bool), Bool(bool),
Char(char), Char(char),
Int(u128), Int(u128),
Float(u64),
String(String), String(String),
} }
@@ -79,7 +81,6 @@ pub struct Item {
/// What kind of [Item] is this? /// What kind of [Item] is this?
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemKind { pub enum ItemKind {
// TODO: Import declaration ("use") item
// TODO: Trait declaration ("trait") item? // TODO: Trait declaration ("trait") item?
/// A [module](Module) /// A [module](Module)
Module(Module), Module(Module),
@@ -258,7 +259,6 @@ pub enum TyKind {
Tuple(TyTuple), Tuple(TyTuple),
Ref(TyRef), Ref(TyRef),
Fn(TyFn), Fn(TyFn),
// TODO: slice, array types
} }
/// An array of [`T`](Ty) /// An array of [`T`](Ty)
@@ -323,7 +323,6 @@ pub struct Stmt {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum StmtKind { pub enum StmtKind {
Empty, Empty,
Local(Let),
Item(Box<Item>), Item(Box<Item>),
Expr(Box<Expr>), Expr(Box<Expr>),
} }
@@ -335,15 +334,6 @@ pub enum Semi {
Unterminated, Unterminated,
} }
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Sym,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// An expression, the beating heart of the language /// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Expr { pub struct Expr {
@@ -357,6 +347,12 @@ pub enum ExprKind {
/// An empty expression: `(` `)` /// An empty expression: `(` `)`
#[default] #[default]
Empty, Empty,
/// A backtick-quoted expression
Quote(Quote),
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
Let(Let),
/// A [Match] expression: `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}`
Match(Match),
/// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
Assign(Assign), Assign(Assign),
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
@@ -365,6 +361,8 @@ pub enum ExprKind {
Binary(Binary), Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`] /// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
Unary(Unary), Unary(Unary),
/// A [Cast] expression: [`Expr`] `as` [`Ty`]
Cast(Cast),
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
Member(Member), Member(Member),
/// An Array [Index] expression: a[10, 20, 30] /// An Array [Index] expression: a[10, 20, 30]
@@ -388,8 +386,6 @@ pub enum ExprKind {
Group(Group), Group(Group),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)` /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple), Tuple(Tuple),
/// A [Loop] expression: `loop` [`Block`]
Loop(Loop),
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While), While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]? /// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
@@ -401,9 +397,46 @@ pub enum ExprKind {
/// A [Return] expression `return` [`Expr`]? /// A [Return] expression `return` [`Expr`]?
Return(Return), Return(Return),
/// A continue expression: `continue` /// A continue expression: `continue`
Continue(Continue), Continue,
} }
/// A backtick-quoted subexpression-literal
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Quote {
pub quote: Box<ExprKind>,
}
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Pattern,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Pattern {
Path(Path),
Literal(Literal),
Ref(Mutability, Box<Pattern>),
Tuple(Vec<Pattern>),
Array(Vec<Pattern>),
Struct(Path, Vec<(Path, Option<Pattern>)>),
}
/// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Match {
pub scrutinee: Box<Expr>,
pub arms: Vec<MatchArm>,
}
/// A single arm of a [Match] expression: [`Pattern`] `=>` [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MatchArm(pub Pattern, pub Expr);
/// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign { pub struct Assign {
@@ -478,12 +511,21 @@ pub enum UnaryKind {
Deref, Deref,
Neg, Neg,
Not, Not,
/// A Loop expression: `loop` [`Block`]
Loop,
/// Unused /// Unused
At, At,
/// Unused /// Unused
Tilde, Tilde,
} }
/// A cast expression: [`Expr`] `as` [`Ty`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Cast {
pub head: Box<ExprKind>,
pub ty: Ty,
}
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Member { pub struct Member {
@@ -537,7 +579,6 @@ pub struct ArrayRep {
/// An address-of expression: `&` `mut`? [`Expr`] /// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf { pub struct AddrOf {
pub count: usize,
pub mutable: Mutability, pub mutable: Mutability,
pub expr: Box<ExprKind>, pub expr: Box<ExprKind>,
} }
@@ -560,12 +601,6 @@ pub struct Tuple {
pub exprs: Vec<Expr>, pub exprs: Vec<Expr>,
} }
/// A [Loop] expression: `loop` [`Block`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Loop {
pub body: Box<Expr>,
}
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct While { pub struct While {
@@ -608,7 +643,3 @@ pub struct Break {
pub struct Return { pub struct Return {
pub body: Option<Box<Expr>>, pub body: Option<Box<Expr>>,
} }
/// A continue expression: `continue`
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Continue;

View File

@@ -9,8 +9,24 @@ mod display {
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
fmt::{Display, Write}, fmt::{Display, Write},
io::IsTerminal,
}; };
fn keyword(d: impl Display, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if std::io::stdout().is_terminal() {
write!(f, "\x1b[31m{d}\x1b[0m")
} else {
d.fmt(f)
}
}
fn ident(d: impl Display, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if std::io::stdout().is_terminal() {
write!(f, "\x1b[95m{d}\x1b[0m")
} else {
d.fmt(f)
}
}
fn separate<I: Display, W: Write>( fn separate<I: Display, W: Write>(
iterable: impl IntoIterator<Item = I>, iterable: impl IntoIterator<Item = I>,
sep: &'static str, sep: &'static str,
@@ -30,7 +46,7 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Mutability::Not => Ok(()), Mutability::Not => Ok(()),
Mutability::Mut => "mut ".fmt(f), Mutability::Mut => keyword("ante ", f),
} }
} }
} }
@@ -39,18 +55,28 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Visibility::Private => Ok(()), Visibility::Private => Ok(()),
Visibility::Public => "pub ".fmt(f), Visibility::Public => keyword("lukin ", f),
} }
} }
} }
impl Display for Literal { impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { fn fmt(this: &Literal, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Literal::Bool(v) => v.fmt(f), match this {
Literal::Char(v) => write!(f, "'{v}'"), Literal::Bool(v) => v.fmt(f),
Literal::Int(v) => v.fmt(f), Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
Literal::String(v) => write!(f, "\"{v}\""), Literal::Int(v) => v.fmt(f),
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
}
}
if std::io::stdout().is_terminal() {
write!(f, "\x1b[94m")?;
fmt(self, f)?;
write!(f, "\x1b[0m")
} else {
fmt(self, f)
} }
} }
} }
@@ -76,7 +102,8 @@ mod display {
impl Display for Meta { impl Display for Meta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self; let Self { name, kind } = self;
write!(f, "{name}{kind}") ident(name, f)?;
write!(f, "{kind}")
} }
} }
@@ -118,9 +145,10 @@ mod display {
impl Display for Alias { impl Display for Alias {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to, from } = self; let Self { to, from } = self;
keyword("ijo", f)?;
match from { match from {
Some(from) => write!(f, "type {to} = {from};"), Some(from) => write!(f, " {to} = {from};"),
None => write!(f, "type {to};"), None => write!(f, " {to};"),
} }
} }
} }
@@ -128,21 +156,28 @@ mod display {
impl Display for Const { impl Display for Const {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, ty, init } = self; let Self { name, ty, init } = self;
write!(f, "const {name}: {ty} = {init}") keyword("kiwen ", f)?;
ident(name, f)?;
write!(f, ": {ty} = {init}")
} }
} }
impl Display for Static { impl Display for Static {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self; let Self { mutable, name, ty, init } = self;
write!(f, "static {mutable}{name}: {ty} = {init}") keyword("mute", f)?;
write!(f, " {mutable}")?;
ident(name, f)?;
write!(f, ": {ty} = {init}")
} }
} }
impl Display for Module { impl Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self; let Self { name, kind } = self;
write!(f, "mod {name}{kind}") keyword("selo ", f)?;
ident(name, f)?;
write!(f, "{kind}")
} }
} }
@@ -171,7 +206,10 @@ mod display {
}; };
debug_assert_eq!(bind.len(), types.len()); debug_assert_eq!(bind.len(), types.len());
write!(f, "fn {name} ")?; keyword("nasin", f)?;
" ".fmt(f)?;
ident(name, f)?;
" ".fmt(f)?;
{ {
let mut f = f.delimit(INLINE_PARENS); let mut f = f.delimit(INLINE_PARENS);
for (idx, (arg, ty)) in bind.iter().zip(types.iter()).enumerate() { for (idx, (arg, ty)) in bind.iter().zip(types.iter()).enumerate() {
@@ -194,14 +232,17 @@ mod display {
impl Display for Param { impl Display for Param {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutability, name } = self; let Self { mutability, name } = self;
write!(f, "{mutability}{name}") write!(f, "{mutability}")?;
ident(name, f)
} }
} }
impl Display for Struct { impl Display for Struct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self; let Self { name, kind } = self;
write!(f, "struct {name}{kind}") keyword("lipu ", f)?;
ident(name, f)?;
write!(f, "{kind}")
} }
} }
@@ -218,14 +259,18 @@ mod display {
impl Display for StructMember { impl Display for StructMember {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { vis, name, ty } = self; let Self { vis, name, ty } = self;
write!(f, "{vis}{name}: {ty}") write!(f, "{vis}")?;
ident(name, f)?;
write!(f, ": {ty}")
} }
} }
impl Display for Enum { impl Display for Enum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self; let Self { name, kind } = self;
write!(f, "enum {name}{kind}") keyword("kulupu ", f)?;
ident(name, f)?;
write!(f, "{kind}")
} }
} }
@@ -241,7 +286,8 @@ mod display {
impl Display for Variant { impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self; let Self { name, kind } = self;
write!(f, "{name}{kind}") ident(name, f)?;
write!(f, "{kind}")
} }
} }
@@ -259,7 +305,8 @@ mod display {
impl Display for Impl { impl Display for Impl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { target, body } = self; let Self { target, body } = self;
write!(f, "impl {target} ")?; keyword("insa", f)?;
write!(f, " {target} ")?;
write!(f.delimit(BRACES), "{body}") write!(f.delimit(BRACES), "{body}")
} }
} }
@@ -269,7 +316,9 @@ mod display {
match self { match self {
ImplKind::Type(t) => t.fmt(f), ImplKind::Type(t) => t.fmt(f),
ImplKind::Trait { impl_trait, for_type } => { ImplKind::Trait { impl_trait, for_type } => {
write!(f, "{impl_trait} for {for_type}") write!(f, "{impl_trait} ")?;
keyword("ale", f)?;
write!(f, " {for_type}")
} }
} }
} }
@@ -278,7 +327,8 @@ mod display {
impl Display for Use { impl Display for Use {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, tree } = self; let Self { absolute, tree } = self;
f.write_str(if *absolute { "use ::" } else { "use " })?; keyword("jo", f)?;
f.write_str(if *absolute { " ::" } else { " " })?;
write!(f, "{tree};") write!(f, "{tree};")
} }
} }
@@ -288,8 +338,12 @@ mod display {
match self { match self {
UseTree::Tree(tree) => separate(tree, ", ")(f.delimit(INLINE_BRACES)), UseTree::Tree(tree) => separate(tree, ", ")(f.delimit(INLINE_BRACES)),
UseTree::Path(path, rest) => write!(f, "{path}::{rest}"), UseTree::Path(path, rest) => write!(f, "{path}::{rest}"),
UseTree::Alias(path, name) => write!(f, "{path} as {name}"), UseTree::Alias(path, name) => {
UseTree::Name(name) => write!(f, "{name}"), write!(f, "{path} ")?;
keyword("sama ", f)?;
ident(name, f)
}
UseTree::Name(name) => ident(name, f),
UseTree::Glob => write!(f, "*"), UseTree::Glob => write!(f, "*"),
} }
} }
@@ -349,7 +403,8 @@ mod display {
impl Display for TyFn { impl Display for TyFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { args, rety } = self; let Self { args, rety } = self;
write!(f, "fn {args}")?; keyword("nasin", f)?;
write!(f, " {args}")?;
match rety { match rety {
Some(v) => write!(f, " -> {v}"), Some(v) => write!(f, " -> {v}"),
None => Ok(()), None => Ok(()),
@@ -370,10 +425,10 @@ mod display {
impl Display for PathPart { impl Display for PathPart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
PathPart::SuperKw => "super".fmt(f), PathPart::SuperKw => keyword("mama", f),
PathPart::SelfKw => "self".fmt(f), PathPart::SelfKw => keyword("mi", f),
PathPart::SelfTy => "Self".fmt(f), PathPart::SelfTy => keyword("Mi", f),
PathPart::Ident(id) => id.fmt(f), PathPart::Ident(id) => ident(id, f),
} }
} }
} }
@@ -389,7 +444,6 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
StmtKind::Empty => Ok(()), StmtKind::Empty => Ok(()),
StmtKind::Local(v) => v.fmt(f),
StmtKind::Item(v) => v.fmt(f), StmtKind::Item(v) => v.fmt(f),
StmtKind::Expr(v) => v.fmt(f), StmtKind::Expr(v) => v.fmt(f),
} }
@@ -405,20 +459,6 @@ mod display {
} }
} }
impl Display for Let {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
write!(f, "let {mutable}{name}")?;
if let Some(value) = ty {
write!(f, ": {value}")?;
}
if let Some(value) = init {
write!(f, " = {value}")?;
}
Ok(())
}
}
impl Display for Expr { impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f) self.kind.fmt(f)
@@ -429,10 +469,14 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ExprKind::Empty => "()".fmt(f), ExprKind::Empty => "()".fmt(f),
ExprKind::Quote(v) => v.fmt(f),
ExprKind::Let(v) => v.fmt(f),
ExprKind::Match(v) => v.fmt(f),
ExprKind::Assign(v) => v.fmt(f), ExprKind::Assign(v) => v.fmt(f),
ExprKind::Modify(v) => v.fmt(f), ExprKind::Modify(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f),
ExprKind::Cast(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f), ExprKind::Member(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f), ExprKind::Index(v) => v.fmt(f),
ExprKind::Structor(v) => v.fmt(f), ExprKind::Structor(v) => v.fmt(f),
@@ -444,17 +488,82 @@ mod display {
ExprKind::Block(v) => v.fmt(f), ExprKind::Block(v) => v.fmt(f),
ExprKind::Group(v) => v.fmt(f), ExprKind::Group(v) => v.fmt(f),
ExprKind::Tuple(v) => v.fmt(f), ExprKind::Tuple(v) => v.fmt(f),
ExprKind::Loop(v) => v.fmt(f),
ExprKind::While(v) => v.fmt(f), ExprKind::While(v) => v.fmt(f),
ExprKind::If(v) => v.fmt(f), ExprKind::If(v) => v.fmt(f),
ExprKind::For(v) => v.fmt(f), ExprKind::For(v) => v.fmt(f),
ExprKind::Break(v) => v.fmt(f), ExprKind::Break(v) => v.fmt(f),
ExprKind::Return(v) => v.fmt(f), ExprKind::Return(v) => v.fmt(f),
ExprKind::Continue(_) => "continue".fmt(f), ExprKind::Continue => keyword("tama", f),
} }
} }
} }
impl Display for Quote {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { quote } = self;
write!(f, "`{quote}`")
}
}
impl Display for Let {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
keyword("poki", f)?;
write!(f, " {mutable}")?;
ident(name, f)?;
if let Some(value) = ty {
write!(f, ": {value}")?;
}
if let Some(value) = init {
write!(f, " = {value}")?;
}
Ok(())
}
}
impl Display for Pattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Pattern::Path(path) => path.fmt(f),
Pattern::Literal(literal) => literal.fmt(f),
Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"),
Pattern::Tuple(patterns) => separate(patterns, ", ")(f.delimit(INLINE_PARENS)),
Pattern::Array(patterns) => separate(patterns, ", ")(f.delimit(INLINE_SQUARE)),
Pattern::Struct(path, items) => {
write!(f, "{path}: ")?;
let f = &mut f.delimit(BRACES);
for (idx, (name, item)) in items.iter().enumerate() {
if idx != 0 {
f.write_str(",\n")?;
}
write!(f, "{name}")?;
if let Some(pattern) = item {
write!(f, ": {pattern}")?
}
}
Ok(())
}
}
}
}
impl Display for Match {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { scrutinee, arms } = self;
keyword("seme", f)?;
write!(f, " {scrutinee} ")?;
separate(arms, ",\n")(f.delimit(BRACES))
}
}
impl Display for MatchArm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(pat, expr) = self;
write!(f, "{pat} => {expr}")
}
}
impl Display for Assign { impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self; let Self { parts } = self;
@@ -538,6 +647,7 @@ mod display {
impl Display for UnaryKind { impl Display for UnaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
UnaryKind::Loop => return keyword("awen ", f),
UnaryKind::Deref => "*", UnaryKind::Deref => "*",
UnaryKind::Neg => "-", UnaryKind::Neg => "-",
UnaryKind::Not => "!", UnaryKind::Not => "!",
@@ -548,6 +658,15 @@ mod display {
} }
} }
impl Display for Cast {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, ty } = self;
write!(f, "{head} ")?;
keyword("sama", f)?;
write!(f, " {ty}")
}
}
impl Display for Member { impl Display for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, kind } = self; let Self { head, kind } = self;
@@ -558,9 +677,12 @@ mod display {
impl Display for MemberKind { impl Display for MemberKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
MemberKind::Call(name, args) => write!(f, "{name}{args}"), MemberKind::Call(name, args) => {
MemberKind::Struct(name) => write!(f, "{name}"), ident(name, f)?;
MemberKind::Tuple(name) => write!(f, "{name}"), separate(&args.exprs, ", ")(f.delimit(INLINE_PARENS))
}
MemberKind::Struct(name) => ident(name, f),
MemberKind::Tuple(name) => ident(name, f),
} }
} }
} }
@@ -584,7 +706,7 @@ mod display {
impl Display for Fielder { impl Display for Fielder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, init } = self; let Self { name, init } = self;
write!(f, "{name}")?; ident(name, f)?;
if let Some(init) = init { if let Some(init) = init {
write!(f, ": {init}")?; write!(f, ": {init}")?;
} }
@@ -607,17 +729,19 @@ mod display {
impl Display for AddrOf { impl Display for AddrOf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { count, mutable, expr } = self; let Self { mutable, expr } = self;
for _ in 0..*count { write!(f, "&{mutable}{expr}")
f.write_char('&')?;
}
write!(f, "{mutable}{expr}")
} }
} }
impl Display for Block { impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.stmts, "\n")(f.delimit(BRACES)) let Self { stmts } = self;
match stmts.as_slice() {
[] => "{}".fmt(f),
stmts => separate(stmts, "\n")(f.delimit(BRACES)),
}
} }
} }
@@ -629,42 +753,48 @@ mod display {
impl Display for Tuple { impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.exprs, ", ")(f.delimit(INLINE_PARENS)) let Self { exprs } = self;
}
}
impl Display for Loop { match exprs.as_slice() {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { [] => write!(f, "()"),
let Self { body } = self; [expr] => write!(f, "({expr},)"),
write!(f, "loop {body}") exprs => separate(exprs, ", ")(f.delimit(INLINE_PARENS)),
}
} }
} }
impl Display for While { impl Display for While {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
write!(f, "while {cond} {pass}{fail}") write!(f, "lawa {cond} {pass}{fail}")
} }
} }
impl Display for If { impl Display for If {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
write!(f, "if {cond} {pass}{fail}") keyword("tan", f)?;
write!(f, " {cond} {pass}{fail}")
} }
} }
impl Display for For { impl Display for For {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { bind, cond, pass, fail } = self; let Self { bind, cond, pass, fail } = self;
write!(f, "for {bind} in {cond} {pass}{fail}") keyword("ale", f)?;
write!(f, " {bind} ")?;
keyword("lon", f)?;
write!(f, " {cond} {pass}{fail}")
} }
} }
impl Display for Else { impl Display for Else {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.body { match &self.body {
Some(body) => write!(f, " else {body}"), Some(body) => {
keyword(" taso", f)?;
write!(f, " {body}")
}
_ => Ok(()), _ => Ok(()),
} }
} }
@@ -672,7 +802,7 @@ mod display {
impl Display for Break { impl Display for Break {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "break")?; keyword("pana", f)?;
match &self.body { match &self.body {
Some(body) => write!(f, " {body}"), Some(body) => write!(f, " {body}"),
_ => Ok(()), _ => Ok(()),
@@ -682,19 +812,13 @@ mod display {
impl Display for Return { impl Display for Return {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "return")?; keyword("pini", f)?;
match &self.body { match &self.body {
Some(body) => write!(f, " {body}"), Some(body) => write!(f, " {body}"),
_ => Ok(()), _ => Ok(()),
} }
} }
} }
impl Display for Continue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"continue".fmt(f)
}
}
} }
mod convert { mod convert {
@@ -755,15 +879,18 @@ mod convert {
TyFn => TyKind::Fn, TyFn => TyKind::Fn,
} }
impl From for StmtKind { impl From for StmtKind {
Let => StmtKind::Local,
Item => StmtKind::Item, Item => StmtKind::Item,
Expr => StmtKind::Expr, Expr => StmtKind::Expr,
} }
impl From for ExprKind { impl From for ExprKind {
Let => ExprKind::Let,
Quote => ExprKind::Quote,
Match => ExprKind::Match,
Assign => ExprKind::Assign, Assign => ExprKind::Assign,
Modify => ExprKind::Modify, Modify => ExprKind::Modify,
Binary => ExprKind::Binary, Binary => ExprKind::Binary,
Unary => ExprKind::Unary, Unary => ExprKind::Unary,
Cast => ExprKind::Cast,
Member => ExprKind::Member, Member => ExprKind::Member,
Index => ExprKind::Index, Index => ExprKind::Index,
Path => ExprKind::Path, Path => ExprKind::Path,
@@ -774,13 +901,11 @@ mod convert {
Block => ExprKind::Block, Block => ExprKind::Block,
Group => ExprKind::Group, Group => ExprKind::Group,
Tuple => ExprKind::Tuple, Tuple => ExprKind::Tuple,
Loop => ExprKind::Loop,
While => ExprKind::While, While => ExprKind::While,
If => ExprKind::If, If => ExprKind::If,
For => ExprKind::For, For => ExprKind::For,
Break => ExprKind::Break, Break => ExprKind::Break,
Return => ExprKind::Return, Return => ExprKind::Return,
Continue => ExprKind::Continue,
} }
impl From for Literal { impl From for Literal {
bool => Literal::Bool, bool => Literal::Bool,
@@ -800,6 +925,50 @@ mod convert {
Self { body: Some(value.into()) } Self { body: Some(value.into()) }
} }
} }
impl TryFrom<ExprKind> for Pattern {
type Error = ExprKind;
/// Performs the conversion. On failure, returns the *first* non-pattern subexpression.
fn try_from(value: ExprKind) -> Result<Self, Self::Error> {
Ok(match value {
ExprKind::Literal(literal) => Pattern::Literal(literal),
ExprKind::Path(path) => Pattern::Path(path),
ExprKind::Empty => Pattern::Tuple(vec![]),
ExprKind::Group(Group { expr }) => Pattern::Tuple(vec![Pattern::try_from(*expr)?]),
ExprKind::Tuple(Tuple { exprs }) => Pattern::Tuple(
exprs
.into_iter()
.map(|e| Pattern::try_from(e.kind))
.collect::<Result<_, _>>()?,
),
ExprKind::AddrOf(AddrOf { mutable, expr }) => {
Pattern::Ref(mutable, Box::new(Pattern::try_from(*expr)?))
}
ExprKind::Array(Array { values }) => Pattern::Array(
values
.into_iter()
.map(|e| Pattern::try_from(e.kind))
.collect::<Result<_, _>>()?,
),
// ExprKind::Index(index) => todo!(),
// ExprKind::Member(member) => todo!(),
ExprKind::Structor(Structor { to, init }) => {
let fields = init
.into_iter()
.map(|Fielder { name, init }| {
Ok((
name.into(),
init.map(|i| Pattern::try_from(i.kind)).transpose()?,
))
})
.collect::<Result<_, Self::Error>>()?;
Pattern::Struct(to, fields)
}
err => Err(err)?,
})
}
}
} }
mod path { mod path {
@@ -825,10 +994,26 @@ mod path {
self self
} }
} }
/// Checks whether this path refers to the sinkhole identifier, `_`
pub fn is_sinkhole(&self) -> bool {
if let [PathPart::Ident(id)] = self.parts.as_slice() {
if let "_" = Sym::to_ref(id) {
return true;
}
}
false
}
} }
impl PathPart { impl PathPart {
pub fn from_sym(ident: Sym) -> Self { pub fn from_sym(ident: Sym) -> Self {
Self::Ident(ident) Self::Ident(ident)
} }
} }
impl From<Sym> for Path {
fn from(value: Sym) -> Self {
Self { parts: vec![PathPart::Ident(value)], absolute: false }
}
}
} }

View File

@@ -37,6 +37,9 @@ pub trait Fold {
fn fold_int(&mut self, i: u128) -> u128 { fn fold_int(&mut self, i: u128) -> u128 {
i i
} }
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
f
}
fn fold_string(&mut self, s: String) -> String { fn fold_string(&mut self, s: String) -> String {
s s
} }
@@ -226,15 +229,6 @@ pub trait Fold {
fn fold_semi(&mut self, s: Semi) -> Semi { fn fold_semi(&mut self, s: Semi) -> Semi {
s s
} }
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_sym(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_expr(&mut self, e: Expr) -> Expr { fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e; let Expr { extents, kind } = e;
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
@@ -242,6 +236,56 @@ pub trait Fold {
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind) or_fold_expr_kind(self, kind)
} }
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_pattern(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
match p {
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
Pattern::Ref(mutability, pattern) => Pattern::Ref(
self.fold_mutability(mutability),
Box::new(self.fold_pattern(*pattern)),
),
Pattern::Tuple(patterns) => {
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Array(patterns) => {
Pattern::Array(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Struct(path, items) => Pattern::Struct(
self.fold_path(path),
items
.into_iter()
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
.collect(),
),
}
}
fn fold_match(&mut self, m: Match) -> Match {
let Match { scrutinee, arms } = m;
Match {
scrutinee: self.fold_expr(*scrutinee).into(),
arms: arms
.into_iter()
.map(|arm| self.fold_match_arm(arm))
.collect(),
}
}
fn fold_match_arm(&mut self, a: MatchArm) -> MatchArm {
let MatchArm(pat, expr) = a;
MatchArm(self.fold_pattern(pat), self.fold_expr(expr))
}
fn fold_assign(&mut self, a: Assign) -> Assign { fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = *parts; let (head, tail) = *parts;
@@ -276,6 +320,10 @@ pub trait Fold {
fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind { fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
kind kind
} }
fn fold_cast(&mut self, cast: Cast) -> Cast {
let Cast { head, ty } = cast;
Cast { head: Box::new(self.fold_expr_kind(*head)), ty: self.fold_ty(ty) }
}
fn fold_member(&mut self, m: Member) -> Member { fn fold_member(&mut self, m: Member) -> Member {
let Member { head, kind } = m; let Member { head, kind } = m;
Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) } Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) }
@@ -315,9 +363,8 @@ pub trait Fold {
} }
} }
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf { fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
let AddrOf { count, mutable, expr } = a; let AddrOf { mutable, expr } = a;
AddrOf { AddrOf {
count,
mutable: self.fold_mutability(mutable), mutable: self.fold_mutability(mutable),
expr: Box::new(self.fold_expr_kind(*expr)), expr: Box::new(self.fold_expr_kind(*expr)),
} }
@@ -334,10 +381,6 @@ pub trait Fold {
let Tuple { exprs } = t; let Tuple { exprs } = t;
Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() } Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() }
} }
fn fold_loop(&mut self, l: Loop) -> Loop {
let Loop { body } = l;
Loop { body: Box::new(self.fold_expr(*body)) }
}
fn fold_while(&mut self, w: While) -> While { fn fold_while(&mut self, w: While) -> While {
let While { cond, pass, fail } = w; let While { cond, pass, fail } = w;
While { While {
@@ -375,10 +418,6 @@ pub trait Fold {
let Return { body } = r; let Return { body } = r;
Return { body: body.map(|e| Box::new(self.fold_expr(*e))) } Return { body: body.map(|e| Box::new(self.fold_expr(*e))) }
} }
fn fold_continue(&mut self, c: Continue) -> Continue {
let Continue = c;
Continue
}
} }
#[inline] #[inline]
@@ -388,6 +427,7 @@ pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Litera
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)), Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
Literal::Char(c) => Literal::Char(folder.fold_char(c)), Literal::Char(c) => Literal::Char(folder.fold_char(c)),
Literal::Int(i) => Literal::Int(folder.fold_int(i)), Literal::Int(i) => Literal::Int(folder.fold_int(i)),
Literal::Float(f) => Literal::Float(folder.fold_smuggled_float(f)),
Literal::String(s) => Literal::String(folder.fold_string(s)), Literal::String(s) => Literal::String(folder.fold_string(s)),
} }
} }
@@ -521,7 +561,6 @@ pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind
pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind { pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind {
match kind { match kind {
StmtKind::Empty => StmtKind::Empty, StmtKind::Empty => StmtKind::Empty,
StmtKind::Local(l) => StmtKind::Local(folder.fold_let(l)),
StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))), StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))),
StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))), StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))),
} }
@@ -531,10 +570,14 @@ pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> St
pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind { pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
match kind { match kind {
ExprKind::Empty => ExprKind::Empty, ExprKind::Empty => ExprKind::Empty,
ExprKind::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified
ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)),
ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)),
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)),
ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)), ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)),
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Cast(c) => ExprKind::Cast(folder.fold_cast(c)),
ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)),
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)), ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)), ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)),
@@ -546,13 +589,12 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)), ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)),
ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)), ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)),
ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)), ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)),
ExprKind::Loop(l) => ExprKind::Loop(folder.fold_loop(l)),
ExprKind::While(w) => ExprKind::While(folder.fold_while(w)), ExprKind::While(w) => ExprKind::While(folder.fold_while(w)),
ExprKind::If(i) => ExprKind::If(folder.fold_if(i)), ExprKind::If(i) => ExprKind::If(folder.fold_if(i)),
ExprKind::For(f) => ExprKind::For(folder.fold_for(f)), ExprKind::For(f) => ExprKind::For(folder.fold_for(f)),
ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)), ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)),
ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)), ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)),
ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)), ExprKind::Continue => ExprKind::Continue,
} }
} }
pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind { pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind {

View File

@@ -23,6 +23,7 @@ pub trait Visit<'a>: Sized {
fn visit_bool(&mut self, _b: &'a bool) {} fn visit_bool(&mut self, _b: &'a bool) {}
fn visit_char(&mut self, _c: &'a char) {} fn visit_char(&mut self, _c: &'a char) {}
fn visit_int(&mut self, _i: &'a u128) {} fn visit_int(&mut self, _i: &'a u128) {}
fn visit_smuggled_float(&mut self, _f: &'a u64) {}
fn visit_string(&mut self, _s: &'a str) {} fn visit_string(&mut self, _s: &'a str) {}
fn visit_file(&mut self, f: &'a File) { fn visit_file(&mut self, f: &'a File) {
let File { items } = f; let File { items } = f;
@@ -191,17 +192,6 @@ pub trait Visit<'a>: Sized {
or_visit_stmt_kind(self, kind) or_visit_stmt_kind(self, kind)
} }
fn visit_semi(&mut self, _s: &'a Semi) {} fn visit_semi(&mut self, _s: &'a Semi) {}
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_sym(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_expr(&mut self, e: &'a Expr) { fn visit_expr(&mut self, e: &'a Expr) {
let Expr { extents, kind } = e; let Expr { extents, kind } = e;
self.visit_span(extents); self.visit_span(extents);
@@ -210,6 +200,55 @@ pub trait Visit<'a>: Sized {
fn visit_expr_kind(&mut self, e: &'a ExprKind) { fn visit_expr_kind(&mut self, e: &'a ExprKind) {
or_visit_expr_kind(self, e) or_visit_expr_kind(self, e)
} }
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_pattern(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_pattern(&mut self, p: &'a Pattern) {
match p {
Pattern::Path(path) => self.visit_path(path),
Pattern::Literal(literal) => self.visit_literal(literal),
Pattern::Ref(mutability, pattern) => {
self.visit_mutability(mutability);
self.visit_pattern(pattern);
}
Pattern::Tuple(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Array(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Struct(path, items) => {
self.visit_path(path);
items.iter().for_each(|(_name, bind)| {
bind.as_ref().inspect(|bind| {
self.visit_pattern(bind);
});
});
}
}
}
fn visit_match(&mut self, m: &'a Match) {
let Match { scrutinee, arms } = m;
self.visit_expr(scrutinee);
arms.iter().for_each(|arm| self.visit_match_arm(arm));
}
fn visit_match_arm(&mut self, a: &'a MatchArm) {
let MatchArm(pat, expr) = a;
self.visit_pattern(pat);
self.visit_expr(expr);
}
fn visit_assign(&mut self, a: &'a Assign) { fn visit_assign(&mut self, a: &'a Assign) {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = parts.as_ref(); let (head, tail) = parts.as_ref();
@@ -238,6 +277,11 @@ pub trait Visit<'a>: Sized {
self.visit_expr_kind(tail); self.visit_expr_kind(tail);
} }
fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {} fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {}
fn visit_cast(&mut self, cast: &'a Cast) {
let Cast { head, ty } = cast;
self.visit_expr_kind(head);
self.visit_ty(ty);
}
fn visit_member(&mut self, m: &'a Member) { fn visit_member(&mut self, m: &'a Member) {
let Member { head, kind } = m; let Member { head, kind } = m;
self.visit_expr_kind(head); self.visit_expr_kind(head);
@@ -273,7 +317,7 @@ pub trait Visit<'a>: Sized {
self.visit_expr_kind(repeat); self.visit_expr_kind(repeat);
} }
fn visit_addrof(&mut self, a: &'a AddrOf) { fn visit_addrof(&mut self, a: &'a AddrOf) {
let AddrOf { count: _, mutable, expr } = a; let AddrOf { mutable, expr } = a;
self.visit_mutability(mutable); self.visit_mutability(mutable);
self.visit_expr_kind(expr); self.visit_expr_kind(expr);
} }
@@ -289,10 +333,6 @@ pub trait Visit<'a>: Sized {
let Tuple { exprs } = t; let Tuple { exprs } = t;
exprs.iter().for_each(|e| self.visit_expr(e)) exprs.iter().for_each(|e| self.visit_expr(e))
} }
fn visit_loop(&mut self, l: &'a Loop) {
let Loop { body } = l;
self.visit_expr(body)
}
fn visit_while(&mut self, w: &'a While) { fn visit_while(&mut self, w: &'a While) {
let While { cond, pass, fail } = w; let While { cond, pass, fail } = w;
self.visit_expr(cond); self.visit_expr(cond);
@@ -330,9 +370,7 @@ pub trait Visit<'a>: Sized {
self.visit_expr(body) self.visit_expr(body)
} }
} }
fn visit_continue(&mut self, c: &'a Continue) { fn visit_continue(&mut self) {}
let Continue = c;
}
} }
pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) { pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
@@ -340,6 +378,7 @@ pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
Literal::Bool(b) => visitor.visit_bool(b), Literal::Bool(b) => visitor.visit_bool(b),
Literal::Char(c) => visitor.visit_char(c), Literal::Char(c) => visitor.visit_char(c),
Literal::Int(i) => visitor.visit_int(i), Literal::Int(i) => visitor.visit_int(i),
Literal::Float(f) => visitor.visit_smuggled_float(f),
Literal::String(s) => visitor.visit_string(s), Literal::String(s) => visitor.visit_string(s),
} }
} }
@@ -443,7 +482,6 @@ pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) {
pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) { pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) {
match kind { match kind {
StmtKind::Empty => {} StmtKind::Empty => {}
StmtKind::Local(l) => visitor.visit_let(l),
StmtKind::Item(i) => visitor.visit_item(i), StmtKind::Item(i) => visitor.visit_item(i),
StmtKind::Expr(e) => visitor.visit_expr(e), StmtKind::Expr(e) => visitor.visit_expr(e),
} }
@@ -452,10 +490,14 @@ pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind)
pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
match e { match e {
ExprKind::Empty => {} ExprKind::Empty => {}
ExprKind::Quote(_q) => {} // Quoted expressions are left unvisited
ExprKind::Let(l) => visitor.visit_let(l),
ExprKind::Match(m) => visitor.visit_match(m),
ExprKind::Assign(a) => visitor.visit_assign(a), ExprKind::Assign(a) => visitor.visit_assign(a),
ExprKind::Modify(m) => visitor.visit_modify(m), ExprKind::Modify(m) => visitor.visit_modify(m),
ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u), ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Cast(c) => visitor.visit_cast(c),
ExprKind::Member(m) => visitor.visit_member(m), ExprKind::Member(m) => visitor.visit_member(m),
ExprKind::Index(i) => visitor.visit_index(i), ExprKind::Index(i) => visitor.visit_index(i),
ExprKind::Structor(s) => visitor.visit_structor(s), ExprKind::Structor(s) => visitor.visit_structor(s),
@@ -467,13 +509,12 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
ExprKind::Block(b) => visitor.visit_block(b), ExprKind::Block(b) => visitor.visit_block(b),
ExprKind::Group(g) => visitor.visit_group(g), ExprKind::Group(g) => visitor.visit_group(g),
ExprKind::Tuple(t) => visitor.visit_tuple(t), ExprKind::Tuple(t) => visitor.visit_tuple(t),
ExprKind::Loop(l) => visitor.visit_loop(l),
ExprKind::While(w) => visitor.visit_while(w), ExprKind::While(w) => visitor.visit_while(w),
ExprKind::If(i) => visitor.visit_if(i), ExprKind::If(i) => visitor.visit_if(i),
ExprKind::For(f) => visitor.visit_for(f), ExprKind::For(f) => visitor.visit_for(f),
ExprKind::Break(b) => visitor.visit_break(b), ExprKind::Break(b) => visitor.visit_break(b),
ExprKind::Return(r) => visitor.visit_return(r), ExprKind::Return(r) => visitor.visit_return(r),
ExprKind::Continue(c) => visitor.visit_continue(c), ExprKind::Continue => visitor.visit_continue(),
} }
} }
pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) { pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) {

View File

@@ -26,8 +26,8 @@ fn desugar_while(extents: Span, kind: ExprKind) -> ExprKind {
let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) }; let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) };
let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } }; let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } };
let loop_body = Expr { extents, kind: ExprKind::If(loop_body) }; let loop_body = ExprKind::If(loop_body);
ExprKind::Loop(Loop { body: Box::new(loop_body) }) ExprKind::Unary(Unary { kind: UnaryKind::Loop, tail: Box::new(loop_body) })
} }
_ => kind, _ => kind,
} }

View File

@@ -21,7 +21,7 @@ pub struct Indent<'f, F: Write + ?Sized> {
f: &'f mut F, f: &'f mut F,
} }
impl<'f, F: Write + ?Sized> Write for Indent<'f, F> { impl<F: Write + ?Sized> Write for Indent<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result { fn write_str(&mut self, s: &str) -> std::fmt::Result {
for s in s.split_inclusive('\n') { for s in s.split_inclusive('\n') {
self.f.write_str(s)?; self.f.write_str(s)?;
@@ -45,14 +45,14 @@ impl<'f, F: Write + ?Sized> Delimit<'f, F> {
Self { f, delim } Self { f, delim }
} }
} }
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, F> { impl<F: Write + ?Sized> Drop for Delimit<'_, F> {
fn drop(&mut self) { fn drop(&mut self) {
let Self { f: Indent { f, .. }, delim } = self; let Self { f: Indent { f, .. }, delim } = self;
let _ = f.write_str(delim.close); let _ = f.write_str(delim.close);
} }
} }
impl<'f, F: Write + ?Sized> Write for Delimit<'f, F> { impl<F: Write + ?Sized> Write for Delimit<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result { fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.f.write_str(s) self.f.write_str(s)
} }

View File

@@ -8,6 +8,7 @@
//! - [Assign], [Binary], and [Unary] expressions //! - [Assign], [Binary], and [Unary] expressions
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators //! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers //! - [Ty] and [TyKind]: Type qualifiers
//! - [Pattern]: Pattern matching operators
//! - [Path]: Path expressions //! - [Path]: Path expressions
#![warn(clippy::all)] #![warn(clippy::all)]
#![feature(decl_macro)] #![feature(decl_macro)]

View File

@@ -0,0 +1,56 @@
//! A bare-minimum harness to evaluate a Conlang program
use std::{error::Error, path::PathBuf};
use cl_ast::Expr;
use cl_interpret::{convalue::ConValue, env::Environment};
use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser};
fn main() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args();
let prog = args.next().unwrap();
let Some(path) = args.next().map(PathBuf::from) else {
println!("Usage: {prog} `file.cl` [ args... ]");
return Ok(());
};
let parent = path.parent().unwrap_or("".as_ref());
let code = std::fs::read_to_string(&path)?;
let code = Parser::new(Lexer::new(&code)).parse()?;
let code = match ModuleInliner::new(parent).inline(code) {
Ok(code) => code,
Err((code, ioerrs, perrs)) => {
for (p, err) in ioerrs {
eprintln!("{}:{err}", p.display());
}
for (p, err) in perrs {
eprintln!("{}:{err}", p.display());
}
code
}
};
let mut env = Environment::new();
env.eval(&code)?;
let main = "main".into();
if env.get(main).is_ok() {
let args = args
.flat_map(|arg| {
Parser::new(Lexer::new(&arg))
.parse::<Expr>()
.map(|arg| env.eval(&arg))
})
.collect::<Result<Vec<_>, _>>()?;
match env.call(main, &args)? {
ConValue::Empty => {}
retval => println!("{retval}"),
}
}
Ok(())
}

View File

@@ -1,67 +1,192 @@
//! Implementations of built-in functions #![allow(non_upper_case_globals)]
use super::{ use crate::{
convalue::ConValue, convalue::ConValue,
env::Environment, env::Environment,
error::{Error, IResult}, error::{Error, IResult},
BuiltIn, Callable,
}; };
use cl_ast::Sym;
use std::{ use std::{
io::{stdout, Write}, io::{stdout, Write},
rc::Rc, slice,
}; };
builtins! { /// A function built into the interpreter.
const MISC; #[derive(Clone, Copy)]
/// Unstable variadic format function pub struct Builtin {
pub fn format<_, args> () -> IResult<ConValue> { /// An identifier to be used during registration
use std::fmt::Write; pub name: &'static str,
let mut out = String::new(); /// The signature, displayed when the builtin is printed
for arg in args { pub desc: &'static str,
write!(out, "{arg}").ok(); /// The function to be run when called
} pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
Ok(ConValue::String(out.into())) }
impl Builtin {
/// Constructs a new Builtin
pub const fn new(
name: &'static str,
desc: &'static str,
func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
) -> Builtin {
Builtin { name, desc, func }
} }
/// Unstable variadic print function pub const fn description(&self) -> &'static str {
pub fn print<_, args> () -> IResult<ConValue> { self.desc
let mut out = stdout().lock();
for arg in args {
write!(out, "{arg}").ok();
}
Ok(ConValue::Empty)
}
/// Unstable variadic println function
pub fn println<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
write!(out, "{arg}").ok();
}
writeln!(out).ok();
Ok(ConValue::Empty)
}
/// Prints the [Debug](std::fmt::Debug) version of the input values
pub fn dbg<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
writeln!(out, "{arg:?}").ok();
}
Ok(args.into())
}
/// Dumps info from the environment
pub fn dump<env, _>() -> IResult<ConValue> {
println!("{}", *env);
Ok(ConValue::Empty)
} }
} }
builtins! {
const BINARY; impl std::fmt::Debug for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Builtin")
.field("description", &self.desc)
.finish_non_exhaustive()
}
}
impl super::Callable for Builtin {
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
(self.func)(interpreter, args)
}
fn name(&self) -> cl_ast::Sym {
self.name.into()
}
}
/// Turns a function definition into a [Builtin].
///
/// ```rust
/// # use cl_interpret::{builtin2::builtin, convalue::ConValue};
/// let my_builtin = builtin! {
/// /// Use the `@env` suffix to bind the environment!
/// /// (needed for recursive calls)
/// fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
/// // This is all Rust code!
/// eprintln!("my_builtin({b}, ..)");
/// match rest {
/// [] => Ok(ConValue::Empty),
/// _ => my_builtin(env, rest), // Can be called as a normal function!
/// }
/// }
/// };
/// ```
pub macro builtin(
$(#[$($meta:tt)*])*
fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
) {{
$(#[$($meta)*])*
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
// Set up the builtin! environment
$(let $env = _env;)?
// Allow for single argument `fn foo(args @ ..)` pattern
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
let [$($arg),*] = _args else {
Err($crate::error::Error::TypeError)?
};
$body.map(Into::into)
}
Builtin {
name: stringify!($name),
desc: stringify![builtin fn $name($($arg),*)],
func: &$name,
}
}}
/// Constructs an array of [Builtin]s from pseudo-function definitions
pub macro builtins($(
$(#[$($meta:tt)*])*
fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
)*) {
[$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
}
/// Creates an [Error::BuiltinDebug] using interpolation of runtime expressions.
/// See [std::format].
pub macro error_format ($($t:tt)*) {
$crate::error::Error::BuiltinDebug(format!($($t)*))
}
pub const Builtins: &[Builtin] = &builtins![
/// Unstable variadic format function
fn fmt(args @ ..) {
use std::fmt::Write;
let mut out = String::new();
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
eprintln!("{e}");
}
Ok(out)
}
/// Prints the arguments in-order, with no separators
fn print(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
Ok(())
}
/// Prints the arguments in-order, followed by a newline
fn println(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
writeln!(out).ok();
Ok(())
}
/// Debug-prints the argument, returning a copy
fn dbg(arg) {
println!("{arg:?}");
Ok(arg.clone())
}
/// Debug-prints the argument
fn dbgp(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
Ok(())
}
/// Dumps the environment
fn dump() @env {
println!("{env}");
Ok(())
}
fn builtins() @env {
for builtin in env.builtins().values().flatten() {
println!("{builtin}");
}
Ok(())
}
/// Returns the length of the input list as a [ConValue::Int]
fn len(list) @env {
Ok(match list {
ConValue::Empty => 0,
ConValue::String(s) => s.chars().count() as _,
ConValue::Ref(r) => return len(env, slice::from_ref(r.as_ref())),
ConValue::Array(t) => t.len() as _,
ConValue::Tuple(t) => t.len() as _,
ConValue::RangeExc(start, end) => (end - start) as _,
ConValue::RangeInc(start, end) => (end - start + 1) as _,
_ => Err(Error::TypeError)?,
})
}
fn dump_symbols() {
println!("{}", cl_structures::intern::string_interner::StringInterner::global());
Ok(ConValue::Empty)
}
/// Returns a shark
fn shark() {
Ok('\u{1f988}')
}
];
pub const Math: &[Builtin] = &builtins![
/// Multiplication `a * b` /// Multiplication `a * b`
pub fn mul(lhs, rhs) -> IResult<ConValue> { fn mul(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
@@ -70,7 +195,7 @@ builtins! {
} }
/// Division `a / b` /// Division `a / b`
pub fn div(lhs, rhs) -> IResult<ConValue> { fn div(lhs, rhs) {
Ok(match (lhs, rhs){ Ok(match (lhs, rhs){
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
@@ -79,7 +204,7 @@ builtins! {
} }
/// Remainder `a % b` /// Remainder `a % b`
pub fn rem(lhs, rhs) -> IResult<ConValue> { fn rem(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
@@ -88,7 +213,7 @@ builtins! {
} }
/// Addition `a + b` /// Addition `a + b`
pub fn add(lhs, rhs) -> IResult<ConValue> { fn add(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
@@ -98,7 +223,7 @@ builtins! {
} }
/// Subtraction `a - b` /// Subtraction `a - b`
pub fn sub(lhs, rhs) -> IResult<ConValue> { fn sub(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
@@ -107,7 +232,7 @@ builtins! {
} }
/// Shift Left `a << b` /// Shift Left `a << b`
pub fn shl(lhs, rhs) -> IResult<ConValue> { fn shl(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
@@ -116,7 +241,7 @@ builtins! {
} }
/// Shift Right `a >> b` /// Shift Right `a >> b`
pub fn shr(lhs, rhs) -> IResult<ConValue> { fn shr(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
@@ -125,7 +250,7 @@ builtins! {
} }
/// Bitwise And `a & b` /// Bitwise And `a & b`
pub fn and(lhs, rhs) -> IResult<ConValue> { fn and(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
@@ -135,7 +260,7 @@ builtins! {
} }
/// Bitwise Or `a | b` /// Bitwise Or `a | b`
pub fn or(lhs, rhs) -> IResult<ConValue> { fn or(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
@@ -145,7 +270,7 @@ builtins! {
} }
/// Bitwise Exclusive Or `a ^ b` /// Bitwise Exclusive Or `a ^ b`
pub fn xor(lhs, rhs) -> IResult<ConValue> { fn xor(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
@@ -154,67 +279,34 @@ builtins! {
}) })
} }
/// Tests whether `a < b`
pub fn lt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, <)
}
/// Tests whether `a <= b`
pub fn lt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, <=)
}
/// Tests whether `a == b`
pub fn eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, ==)
}
/// Tests whether `a != b`
pub fn neq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, !=)
}
/// Tests whether `a <= b`
pub fn gt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, >=)
}
/// Tests whether `a < b`
pub fn gt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, >)
}
}
builtins! {
const RANGE;
/// Exclusive Range `a..b` /// Exclusive Range `a..b`
pub fn range_exc(lhs, rhs) -> IResult<ConValue> { fn range_exc(from, to) {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else { let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
Err(Error::TypeError)? Err(Error::TypeError)?
}; };
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1))) Ok(ConValue::RangeExc(from, to))
} }
/// Inclusive Range `a..=b` /// Inclusive Range `a..=b`
pub fn range_inc(lhs, rhs) -> IResult<ConValue> { fn range_inc(from, to) {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else { let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
Err(Error::TypeError)? Err(Error::TypeError)?
}; };
Ok(ConValue::RangeInc(lhs, rhs)) Ok(ConValue::RangeInc(from, to))
} }
}
builtins! {
const UNARY;
/// Negates the ConValue /// Negates the ConValue
pub fn neg(tail) -> IResult<ConValue> { fn neg(tail) {
Ok(match tail { Ok(match tail {
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()), ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
ConValue::Float(v) => ConValue::Float(-v),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError)?,
}) })
} }
/// Inverts the ConValue /// Inverts the ConValue
pub fn not(tail) -> IResult<ConValue> { fn not(tail) {
Ok(match tail { Ok(match tail {
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(!v), ConValue::Int(v) => ConValue::Int(!v),
@@ -223,62 +315,23 @@ builtins! {
}) })
} }
pub fn deref(tail) -> IResult<ConValue> { /// Compares two values
fn cmp(head, tail) {
Ok(ConValue::Int(match (head, tail) {
(ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
(ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
(ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
(ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
_ => Err(error_format!("Incomparable values: {head}, {tail}"))?
}))
}
/// Does the opposite of `&`
fn deref(tail) {
use std::rc::Rc;
Ok(match tail { Ok(match tail {
ConValue::Ref(v) => Rc::as_ref(v).clone(), ConValue::Ref(v) => Rc::as_ref(v).clone(),
_ => tail.clone(), _ => tail.clone(),
}) })
} }
} ];
/// Turns an argument slice into an array with the (inferred) correct number of elements
pub fn to_args<const N: usize>(args: &[ConValue]) -> IResult<&[ConValue; N]> {
args.try_into()
.map_err(|_| Error::ArgNumber { want: N, got: args.len() })
}
/// Turns function definitions into ZSTs which implement [Callable] and [BuiltIn]
macro builtins (
$(prefix = $prefix:literal)?
const $defaults:ident $( = [$($additional_builtins:expr),*$(,)?])?;
$(
$(#[$meta:meta])*$vis:vis fn $name:ident$(<$env:tt, $args:tt>)? ( $($($arg:tt),+$(,)?)? ) $(-> $rety:ty)?
$body:block
)*
) {
/// Builtins to load when a new interpreter is created
pub const $defaults: &[&dyn BuiltIn] = &[$(&$name,)* $($additional_builtins)*];
$(
$(#[$meta])* #[allow(non_camel_case_types)] #[derive(Clone, Debug)]
/// ```rust,ignore
#[doc = stringify!(builtin! fn $name($($($arg),*)?) $(-> $rety)? $body)]
/// ```
$vis struct $name;
impl BuiltIn for $name {
fn description(&self) -> &str { concat!("builtin ", stringify!($name), stringify!(($($($arg),*)?) )) }
}
impl Callable for $name {
#[allow(unused)]
fn call(&self, env: &mut Environment, args: &[ConValue]) $(-> $rety)? {
// println!("{}", stringify!($name), );
$(let $env = env;
let $args = args;)?
$(let [$($arg),*] = to_args(args)?;)?
$body
}
fn name(&self) -> Sym { stringify!($name).into() }
}
)*
}
/// Templates comparison functions for [ConValue]
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
match ($a, $b) {
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(&**a $op &**b)),
_ => Err(Error::TypeError)
}
}

View File

@@ -0,0 +1,336 @@
//! Values in the dynamically typed AST interpreter.
//!
//! The most permanent fix is a temporary one.
use cl_ast::{format::FmtAdapter, ExprKind, Sym};
use super::{
builtin::Builtin,
error::{Error, IResult},
function::Function, Callable, Environment,
};
use std::{collections::HashMap, ops::*, rc::Rc};
type Integer = isize;
/// A Conlang value stores data in the interpreter
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A floating point number
Float(f64),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string
String(Sym),
/// A reference
Ref(Rc<ConValue>),
/// An Array
Array(Box<[ConValue]>),
/// A tuple
Tuple(Box<[ConValue]>),
/// An exclusive range
RangeExc(Integer, Integer),
/// An inclusive range
RangeInc(Integer, Integer),
/// A value of a product type
Struct(Box<(Sym, HashMap<Sym, ConValue>)>),
/// An entire namespace
Module(Box<HashMap<Sym, Option<ConValue>>>),
/// A quoted expression
Quote(Box<ExprKind>),
/// A callable thing
Function(Rc<Function>),
/// A built-in function
Builtin(&'static Builtin),
}
impl ConValue {
/// Gets whether the current value is true or false
pub fn truthy(&self) -> IResult<bool> {
match self {
ConValue::Bool(v) => Ok(*v),
_ => Err(Error::TypeError)?,
}
}
pub fn range_exc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeExc(a, b))
}
pub fn range_inc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeInc(a, b))
}
pub fn index(&self, index: &Self) -> IResult<ConValue> {
let Self::Int(index) = index else {
Err(Error::TypeError)?
};
match self {
ConValue::String(string) => string
.chars()
.nth(*index as _)
.map(ConValue::Char)
.ok_or(Error::OobIndex(*index as usize, string.chars().count())),
ConValue::Array(arr) => arr
.get(*index as usize)
.cloned()
.ok_or(Error::OobIndex(*index as usize, arr.len())),
_ => Err(Error::TypeError),
}
}
cmp! {
lt: false, <;
lt_eq: true, <=;
eq: true, ==;
neq: false, !=;
gt_eq: true, >=;
gt: false, >;
}
assign! {
add_assign: +;
bitand_assign: &;
bitor_assign: |;
bitxor_assign: ^;
div_assign: /;
mul_assign: *;
rem_assign: %;
shl_assign: <<;
shr_assign: >>;
sub_assign: -;
}
}
impl Callable for ConValue {
fn name(&self) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::Builtin(func) => func.name(),
_ => "".into(),
}
}
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
match self {
Self::Function(func) => func.call(interpreter, args),
Self::Builtin(func) => func.call(interpreter, args),
_ => Err(Error::NotCallable(self.clone())),
}
}
}
/// Templates comparison functions for [ConValue]
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
/// TODO: Remove when functions are implemented:
/// Desugar into function calls
pub fn $fn(&self, other: &Self) -> IResult<Self> {
match (self, other) {
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
(Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
_ => Err(Error::TypeError)
}
}
)*}
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
pub fn $fn(&mut self, other: Self) -> IResult<()> {
*self = (std::mem::take(self) $op other)?;
Ok(())
}
)*}
/// Implements [From] for an enum with 1-tuple variants
macro from ($($T:ty => $v:expr),*$(,)?) {
$(impl From<$T> for ConValue {
fn from(value: $T) -> Self { $v(value.into()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::String(*value)
}
}
from! {
Integer => ConValue::Int,
f64 => ConValue::Float,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::String,
&str => ConValue::String,
String => ConValue::String,
Rc<str> => ConValue::String,
ExprKind => ConValue::Quote,
Function => ConValue::Function,
Vec<ConValue> => ConValue::Tuple,
&'static Builtin => ConValue::Builtin,
}
impl From<()> for ConValue {
fn from(_: ()) -> Self {
Self::Empty
}
}
impl From<&[ConValue]> for ConValue {
fn from(value: &[ConValue]) -> Self {
match value.len() {
0 => Self::Empty,
1 => value[0].clone(),
_ => Self::Tuple(value.into()),
}
}
}
/// Implements binary [std::ops] traits for [ConValue]
///
/// TODO: Desugar operators into function calls
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
$(impl $trait for ConValue {
type Output = IResult<Self>;
/// TODO: Desugar operators into function calls
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
})*
}
ops! {
Add: add = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>().into())
}
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} / {b}"); a
})),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
_ => Err(Error::TypeError)?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
_ => Err(Error::TypeError)?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
println!("Warning: Divide by zero in {a} % {b}"); a
})),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
_ => Err(Error::TypeError)?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError)?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError)?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
_ => Err(Error::TypeError)?
]
}
impl std::fmt::Display for ConValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConValue::Empty => "Empty".fmt(f),
ConValue::Int(v) => v.fmt(f),
ConValue::Float(v) => v.fmt(f),
ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => v.fmt(f),
ConValue::String(v) => v.fmt(f),
ConValue::Ref(v) => write!(f, "&{v}"),
ConValue::Array(array) => {
'['.fmt(f)?;
for (idx, element) in array.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
']'.fmt(f)
}
ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1),
ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"),
ConValue::Tuple(tuple) => {
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::Struct(parts) => {
let (name, map) = parts.as_ref();
use std::fmt::Write;
if !name.is_empty() {
write!(f, "{name}: ")?;
}
let mut f = f.delimit_with("{", "\n}");
for (k, v) in map.iter() {
write!(f, "\n{k}: {v},")?;
}
Ok(())
}
ConValue::Module(module) => {
use std::fmt::Write;
let mut f = f.delimit_with("{", "\n}");
for (k, v) in module.iter() {
write!(f, "\n{k}: ")?;
match v {
Some(v) => write!(f, "{v},"),
None => write!(f, "_,"),
}?
}
Ok(())
}
ConValue::Quote(q) => {
write!(f, "`{q}`")
}
ConValue::Function(func) => {
write!(f, "{}", func.decl())
}
ConValue::Builtin(func) => {
write!(f, "{}", func.description())
}
}
}
}

View File

@@ -0,0 +1,236 @@
//! Lexical and non-lexical scoping for variables
use crate::builtin::Builtin;
use super::{
builtin::{Builtins, Math},
convalue::ConValue,
error::{Error, IResult},
function::Function,
Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
rc::Rc,
};
type StackFrame = HashMap<Sym, Option<ConValue>>;
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
builtin: StackFrame,
global: Vec<(StackFrame, &'static str)>,
frames: Vec<(StackFrame, &'static str)>,
}
impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (frame, name) in self
.global
.iter()
.rev()
.take(2)
.rev()
.chain(self.frames.iter())
{
writeln!(f, "--- {name} ---")?;
for (var, val) in frame {
write!(f, "{var}: ")?;
match val {
Some(value) => writeln!(f, "\t{value}"),
None => writeln!(f, "<undefined>"),
}?
}
}
Ok(())
}
}
impl Default for Environment {
fn default() -> Self {
Self {
builtin: to_hashmap(Builtins.iter().chain(Math.iter())),
global: vec![(HashMap::new(), "globals")],
frames: vec![],
}
}
}
fn to_hashmap(from: impl IntoIterator<Item = &'static Builtin>) -> HashMap<Sym, Option<ConValue>> {
from.into_iter()
.map(|v| (v.name(), Some(v.into())))
.collect()
}
impl Environment {
pub fn new() -> Self {
Self::default()
}
/// Creates an [Environment] with no [builtins](super::builtin)
pub fn no_builtins() -> Self {
Self {
builtin: HashMap::new(),
global: vec![(Default::default(), "globals")],
frames: vec![],
}
}
pub fn builtins(&self) -> &StackFrame {
&self.builtin
}
pub fn add_builtin(&mut self, builtin: &'static Builtin) -> &mut Self {
self.builtin.insert(builtin.name(), Some(builtin.into()));
self
}
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
for builtin in builtins {
self.add_builtin(builtin);
}
}
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
self.frames.push((frame, name));
}
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
self.frames.pop()
}
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
node.interpret(self)
}
/// Calls a function inside the interpreter's scope,
/// and returns the result
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
// FIXME: Clone to satisfy the borrow checker
let function = self.get(name)?.clone();
function.call(self, args)
}
/// Enters a nested scope, returning a [`Frame`] stack-guard.
///
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn frame(&mut self, name: &'static str) -> Frame {
Frame::new(self, name)
}
/// Resolves a variable mutably.
///
/// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, id: Sym) -> IResult<&mut Option<ConValue>> {
for (frame, _) in self.frames.iter_mut().rev() {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
for (frame, _) in self.global.iter_mut().rev() {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
self.builtin.get_mut(&id).ok_or(Error::NotDefined(id))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
for (frame, _) in self.global.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
self.builtin
.get(&id)
.cloned()
.flatten()
.ok_or(Error::NotDefined(id))
}
pub(crate) fn get_local(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotInitialized(id))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id, value);
} else if let Some((frame, _)) = self.global.last_mut() {
frame.insert(id, value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl;
let (name, function) = (name, Rc::new(Function::new(decl)));
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(*name, Some(ConValue::Function(function.clone())));
} else if let Some((frame, _)) = self.global.last_mut() {
frame.insert(*name, Some(ConValue::Function(function.clone())));
}
// Tell the function to lift its upvars now, after it's been declared
function.lift_upvars(self);
}
}
/// Functions which aid in the implementation of [`Frame`]
impl Environment {
/// Enters a scope, creating a new namespace for variables
fn enter(&mut self, name: &'static str) -> &mut Self {
self.frames.push((Default::default(), name));
self
}
/// Exits the scope, destroying all local variables and
/// returning the outer scope, if there is one
fn exit(&mut self) -> &mut Self {
self.frames.pop();
self
}
}
/// Represents a stack frame
#[derive(Debug)]
pub struct Frame<'scope> {
scope: &'scope mut Environment,
}
impl<'scope> Frame<'scope> {
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
Self { scope: scope.enter(name) }
}
}
impl Deref for Frame<'_> {
type Target = Environment;
fn deref(&self) -> &Self::Target {
self.scope
}
}
impl DerefMut for Frame<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.scope
}
}
impl Drop for Frame<'_> {
fn drop(&mut self) {
self.scope.exit();
}
}

View File

@@ -0,0 +1,97 @@
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::{Pattern, Sym};
use super::convalue::ConValue;
pub type IResult<T> = Result<T, Error>;
/// Represents any error thrown by the [Environment](super::Environment)
#[derive(Clone, Debug)]
pub enum Error {
/// Propagate a Return value
Return(ConValue),
/// Propagate a Break value
Break(ConValue),
/// Break propagated across function bounds
BadBreak(ConValue),
/// Continue to the next iteration of a loop
Continue,
/// Underflowed the stack
StackUnderflow,
/// Exited the last scope
ScopeExit,
/// Type incompatibility
// TODO: store the type information in this error
TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
/// A value could not be indexed
NotIndexable,
/// An array index went out of bounds
OobIndex(usize, usize),
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(Sym),
/// A name was defined but not initialized
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber { want: usize, got: usize },
/// A pattern failed to match
PatFailed(Box<Pattern>),
/// Fell through a non-exhaustive match
MatchNonexhaustive,
/// Error produced by a Builtin
BuiltinDebug(String),
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Return(value) => write!(f, "return {value}"),
Error::Break(value) => write!(f, "break {value}"),
Error::BadBreak(value) => write!(f, "rogue break: {value}"),
Error::Continue => "continue".fmt(f),
Error::StackUnderflow => "Stack underflow".fmt(f),
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
Error::TypeError => "Incompatible types".fmt(f),
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
Error::NotIndexable => {
write!(f, "expression cannot be indexed")
}
Error::OobIndex(idx, len) => {
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
}
Error::NotAssignable => {
write!(f, "expression is not assignable")
}
Error::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?")
}
Error::NotInitialized(value) => {
write!(f, "{value} bound, but not initialized")
}
Error::NotCallable(value) => {
write!(f, "{value} is not callable.")
}
Error::ArgNumber { want, got } => {
write!(
f,
"Expected {want} argument{}, got {got}",
if *want == 1 { "" } else { "s" }
)
}
Error::PatFailed(pattern) => {
write!(f, "Failed to match pattern {pattern}")
}
Error::MatchNonexhaustive => {
write!(f, "Fell through a non-exhaustive match expression!")
}
Error::BuiltinDebug(s) => write!(f, "DEBUG: {s}"),
}
}
}

View File

@@ -0,0 +1,80 @@
//! Represents a block of code which lives inside the Interpreter
use collect_upvars::collect_upvars;
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Param, Sym};
use std::{
cell::{Ref, RefCell},
collections::HashMap,
rc::Rc,
};
pub mod collect_upvars;
type Upvars = HashMap<Sym, Option<ConValue>>;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Rc<FnDecl>,
/// Stores data from the enclosing scopes
upvars: RefCell<Upvars>,
}
impl Function {
pub fn new(decl: &FnDecl) -> Self {
// let upvars = collect_upvars(decl, env);
Self { decl: decl.clone().into(), upvars: Default::default() }
}
pub fn decl(&self) -> &FnDecl {
&self.decl
}
pub fn upvars(&self) -> Ref<Upvars> {
self.upvars.borrow()
}
pub fn lift_upvars(&self, env: &Environment) {
let upvars = collect_upvars(&self.decl, env);
if let Ok(mut self_upvars) = self.upvars.try_borrow_mut() {
*self_upvars = upvars;
}
}
}
impl Callable for Function {
fn name(&self) -> Sym {
let FnDecl { name, .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
// Check arg mapping
if args.len() != bind.len() {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(*name));
};
let upvars = self.upvars.take();
env.push_frame("upvars", upvars);
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name }, value) in bind.iter().zip(args) {
frame.insert(*name, Some(value.clone()));
}
let res = body.interpret(&mut frame);
drop(frame);
if let Some((upvars, _)) = env.pop_frame() {
self.upvars.replace(upvars);
}
match res {
Err(Error::Return(value)) => Ok(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
result => result,
}
}
}

View File

@@ -0,0 +1,134 @@
//! Collects the "Upvars" of a function at the point of its creation, allowing variable capture
use crate::{convalue::ConValue, env::Environment};
use cl_ast::{ast_visitor::visit::*, Function, Let, Param, Path, PathPart, Pattern, Sym};
use std::collections::{HashMap, HashSet};
pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars {
CollectUpvars::new(env).get_upvars(f)
}
#[derive(Clone, Debug)]
pub struct CollectUpvars<'env> {
env: &'env Environment,
upvars: HashMap<Sym, Option<ConValue>>,
blacklist: HashSet<Sym>,
}
impl<'env> CollectUpvars<'env> {
pub fn new(env: &'env Environment) -> Self {
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
}
pub fn get_upvars(mut self, f: &cl_ast::Function) -> HashMap<Sym, Option<ConValue>> {
self.visit_function(f);
self.upvars
}
pub fn add_upvar(&mut self, name: &Sym) {
let Self { env, upvars, blacklist } = self;
if blacklist.contains(name) || upvars.contains_key(name) {
return;
}
if let Ok(upvar) = env.get_local(*name) {
upvars.insert(*name, Some(upvar));
}
}
pub fn bind_name(&mut self, name: &Sym) {
self.blacklist.insert(*name);
}
}
impl<'a> Visit<'a> for CollectUpvars<'_> {
fn visit_block(&mut self, b: &'a cl_ast::Block) {
let blacklist = self.blacklist.clone();
// visit the block
let cl_ast::Block { stmts } = b;
stmts.iter().for_each(|s| self.visit_stmt(s));
// restore the blacklist
self.blacklist = blacklist;
}
fn visit_let(&mut self, l: &'a cl_ast::Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
if let Some(ty) = ty {
self.visit_ty(ty);
}
// visit the initializer, which may use the bound name
if let Some(init) = init {
self.visit_expr(init)
}
// a bound name can never be an upvar
self.visit_pattern(name);
}
fn visit_function(&mut self, f: &'a cl_ast::Function) {
let Function { name: _, sign: _, bind, body } = f;
// parameters can never be upvars
for Param { mutability: _, name } in bind {
self.bind_name(name);
}
if let Some(body) = body {
self.visit_block(body);
}
}
fn visit_for(&mut self, f: &'a cl_ast::For) {
let cl_ast::For { bind, cond, pass, fail } = f;
self.visit_expr(cond);
self.visit_else(fail);
self.bind_name(bind); // TODO: is bind only bound in the pass block?
self.visit_block(pass);
}
fn visit_path(&mut self, p: &'a cl_ast::Path) {
// TODO: path resolution in environments
let Path { absolute: false, parts } = p else {
return;
};
let [PathPart::Ident(name)] = parts.as_slice() else {
return;
};
self.add_upvar(name);
}
fn visit_fielder(&mut self, f: &'a cl_ast::Fielder) {
let cl_ast::Fielder { name, init } = f;
if let Some(init) = init {
self.visit_expr(init);
} else {
self.add_upvar(name); // fielder without init grabs from env
}
}
fn visit_pattern(&mut self, p: &'a cl_ast::Pattern) {
match p {
Pattern::Path(path) => {
if let [PathPart::Ident(name)] = path.parts.as_slice() {
self.bind_name(name)
}
}
Pattern::Literal(literal) => self.visit_literal(literal),
Pattern::Ref(mutability, pattern) => {
self.visit_mutability(mutability);
self.visit_pattern(pattern);
}
Pattern::Tuple(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Array(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Struct(path, items) => {
self.visit_path(path);
items.iter().for_each(|(_name, bind)| {
bind.as_ref().inspect(|bind| {
self.visit_pattern(bind);
});
});
}
}
}
}

View File

@@ -9,6 +9,7 @@ use std::{borrow::Borrow, rc::Rc};
use super::*; use super::*;
use cl_ast::*; use cl_ast::*;
use cl_structures::intern::interned::Interned;
/// A work-in-progress tree walk interpreter for Conlang /// A work-in-progress tree walk interpreter for Conlang
pub trait Interpret { pub trait Interpret {
/// Interprets this thing in the given [`Environment`]. /// Interprets this thing in the given [`Environment`].
@@ -36,7 +37,7 @@ impl Interpret for Item {
ItemKind::Struct(item) => item.interpret(env), ItemKind::Struct(item) => item.interpret(env),
ItemKind::Enum(item) => item.interpret(env), ItemKind::Enum(item) => item.interpret(env),
ItemKind::Impl(item) => item.interpret(env), ItemKind::Impl(item) => item.interpret(env),
ItemKind::Use(_) => todo!("namespaces and imports in the interpreter"), ItemKind::Use(item) => item.interpret(env),
} }
} }
} }
@@ -67,11 +68,21 @@ impl Interpret for Static {
impl Interpret for Module { impl Interpret for Module {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { name, kind } = self; let Self { name, kind } = self;
// TODO: Enter this module's namespace env.push_frame(Interned::to_ref(name), Default::default());
match kind { let out = match kind {
ModuleKind::Inline(file) => file.interpret(env), ModuleKind::Inline(file) => file.interpret(env),
ModuleKind::Outline => Err(Error::Outlined(*name)), ModuleKind::Outline => {
} eprintln!("Module {name} specified, but not imported.");
Ok(ConValue::Empty)
}
};
let (frame, _) = env
.pop_frame()
.expect("Environment frames must be balanced");
env.insert(*name, Some(ConValue::Module(frame.into())));
out
} }
} }
impl Interpret for Function { impl Interpret for Function {
@@ -100,12 +111,78 @@ impl Interpret for Impl {
body.interpret(env) body.interpret(env)
} }
} }
impl Interpret for Use {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { absolute: _, tree } = self;
tree.interpret(env)
}
}
impl Interpret for UseTree {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
type Bindings = HashMap<Sym, ConValue>;
use std::collections::HashMap;
fn get_bindings(
tree: &UseTree,
env: &mut Environment,
bindings: &mut Bindings,
) -> IResult<()> {
match tree {
UseTree::Tree(use_trees) => {
for tree in use_trees {
get_bindings(tree, env, bindings)?;
}
}
UseTree::Path(PathPart::Ident(name), tree) => {
let Ok(ConValue::Module(m)) = env.get(*name) else {
Err(Error::TypeError)?
};
env.push_frame(Interned::to_ref(name), *m);
let out = get_bindings(tree, env, bindings);
env.pop_frame();
return out;
}
UseTree::Alias(name, alias) => {
bindings.insert(*alias, env.get(*name)?);
}
UseTree::Name(name) => {
bindings.insert(*name, env.get(*name)?);
}
UseTree::Glob => {
if let Some((frame, name)) = env.pop_frame() {
for (k, v) in &frame {
if let Some(v) = v {
bindings.insert(*k, v.clone());
}
}
env.push_frame(name, frame);
}
}
other => {
eprintln!("ERROR: Cannot use {other}");
}
}
Ok(())
}
let mut bindings = Bindings::new();
get_bindings(self, env, &mut bindings)?;
for (name, value) in bindings {
env.insert(name, Some(value));
}
Ok(ConValue::Empty)
}
}
impl Interpret for Stmt { impl Interpret for Stmt {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind, semi } = self; let Self { extents: _, kind, semi } = self;
let out = match kind { let out = match kind {
StmtKind::Empty => ConValue::Empty, StmtKind::Empty => ConValue::Empty,
StmtKind::Local(stmt) => stmt.interpret(env)?,
StmtKind::Item(stmt) => stmt.interpret(env)?, StmtKind::Item(stmt) => stmt.interpret(env)?,
StmtKind::Expr(stmt) => stmt.interpret(env)?, StmtKind::Expr(stmt) => stmt.interpret(env)?,
}; };
@@ -115,14 +192,7 @@ impl Interpret for Stmt {
}) })
} }
} }
impl Interpret for Let {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Let { mutable: _, name, ty: _, init } = self;
let init = init.as_ref().map(|i| i.interpret(env)).transpose()?;
env.insert(*name, init);
Ok(ConValue::Empty)
}
}
impl Interpret for Expr { impl Interpret for Expr {
#[inline] #[inline]
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
@@ -130,14 +200,19 @@ impl Interpret for Expr {
kind.interpret(env) kind.interpret(env)
} }
} }
impl Interpret for ExprKind { impl Interpret for ExprKind {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match self { match self {
ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Quote(q) => q.interpret(env),
ExprKind::Let(v) => v.interpret(env),
ExprKind::Match(v) => v.interpret(env),
ExprKind::Assign(v) => v.interpret(env), ExprKind::Assign(v) => v.interpret(env),
ExprKind::Modify(v) => v.interpret(env), ExprKind::Modify(v) => v.interpret(env),
ExprKind::Binary(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env),
ExprKind::Unary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env),
ExprKind::Cast(v) => v.interpret(env),
ExprKind::Member(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env),
ExprKind::Index(v) => v.interpret(env), ExprKind::Index(v) => v.interpret(env),
ExprKind::Structor(v) => v.interpret(env), ExprKind::Structor(v) => v.interpret(env),
@@ -149,36 +224,306 @@ impl Interpret for ExprKind {
ExprKind::Block(v) => v.interpret(env), ExprKind::Block(v) => v.interpret(env),
ExprKind::Group(v) => v.interpret(env), ExprKind::Group(v) => v.interpret(env),
ExprKind::Tuple(v) => v.interpret(env), ExprKind::Tuple(v) => v.interpret(env),
ExprKind::Loop(v) => v.interpret(env),
ExprKind::While(v) => v.interpret(env), ExprKind::While(v) => v.interpret(env),
ExprKind::If(v) => v.interpret(env), ExprKind::If(v) => v.interpret(env),
ExprKind::For(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env),
ExprKind::Break(v) => v.interpret(env), ExprKind::Break(v) => v.interpret(env),
ExprKind::Return(v) => v.interpret(env), ExprKind::Return(v) => v.interpret(env),
ExprKind::Continue(v) => v.interpret(env), ExprKind::Continue => Err(Error::Continue),
} }
} }
} }
fn evaluate_place_expr<'e>( impl Interpret for Quote {
env: &'e mut Environment, fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
expr: &ExprKind, // TODO: squoosh down into a ConValue?
) -> IResult<(&'e mut Option<ConValue>, Sym)> { Ok(ConValue::Quote(self.quote.clone()))
match expr { }
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => { }
match parts.last().expect("parts should not be empty") {
PathPart::SuperKw => Err(Error::NotAssignable), impl Interpret for Let {
PathPart::SelfKw => todo!("Assignment to `self`"), fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
PathPart::SelfTy => todo!("What does it mean to assign to capital-S Self?"), let Let { mutable: _, name, ty: _, init } = self;
PathPart::Ident(s) => env.get_mut(*s).map(|v| (v, *s)), match init.as_ref().map(|i| i.interpret(env)).transpose()? {
Some(value) => {
for (path, value) in assignment::pattern_substitution(name, value)? {
match path.parts.as_slice() {
[PathPart::Ident(name)] => env.insert(*name, Some(value)),
_ => eprintln!("Bad assignment: {path} = {value}"),
}
}
}
None => {
for path in assignment::pattern_variables(name) {
match path.parts.as_slice() {
[PathPart::Ident(name)] => env.insert(*name, None),
_ => eprintln!("Bad assignment: {path}"),
}
}
} }
} }
ExprKind::Index(_) => todo!("Assignment to an index operation"), Ok(ConValue::Empty)
ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"), }
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => { }
todo!("Pattern Destructuring?")
impl Interpret for Match {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { scrutinee, arms } = self;
let scrutinee = scrutinee.interpret(env)?;
'arm: for MatchArm(pat, expr) in arms {
if let Ok(substitution) = assignment::pattern_substitution(pat, scrutinee.clone()) {
let mut env = env.frame("match");
for (path, value) in substitution {
let [PathPart::Ident(name)] = path.parts.as_slice() else {
continue 'arm;
};
env.insert(*name, Some(value));
}
return expr.interpret(&mut env);
}
}
Err(Error::MatchNonexhaustive)
}
}
mod assignment {
/// Pattern matching engine for assignment
use super::*;
use std::collections::HashMap;
type Namespace = HashMap<Sym, Option<ConValue>>;
/// Gets the path variables in the given Pattern
pub fn pattern_variables(pat: &Pattern) -> Vec<&Path> {
fn patvars<'p>(set: &mut Vec<&'p Path>, pat: &'p Pattern) {
match pat {
Pattern::Path(path) if path.is_sinkhole() => {}
Pattern::Path(path) => set.push(path),
Pattern::Literal(_) => {}
Pattern::Ref(_, pattern) => patvars(set, pattern),
Pattern::Tuple(patterns) | Pattern::Array(patterns) => {
patterns.iter().for_each(|pat| patvars(set, pat))
}
Pattern::Struct(_path, items) => {
items.iter().for_each(|(name, pat)| match pat {
Some(pat) => patvars(set, pat),
None => set.push(name),
});
}
}
}
let mut set = Vec::new();
patvars(&mut set, pat);
set
}
/// Appends a substitution to the provided table
pub fn append_sub<'pat>(
env: &mut HashMap<&'pat Path, ConValue>,
pat: &'pat Pattern,
value: ConValue,
) -> IResult<()> {
match pat {
Pattern::Path(path) if path.is_sinkhole() => Ok(()),
Pattern::Path(path) => {
env.insert(path, value);
Ok(())
}
Pattern::Literal(literal) => match (literal, value) {
(Literal::Bool(a), ConValue::Bool(b)) => *a == b,
(Literal::Char(a), ConValue::Char(b)) => *a == b,
(Literal::Int(a), ConValue::Int(b)) => *a as isize == b,
(Literal::Float(a), ConValue::Float(b)) => f64::from_bits(*a) == b,
(Literal::String(a), ConValue::String(b)) => *a == *b,
_ => false,
}
.then_some(())
.ok_or(Error::NotAssignable),
Pattern::Ref(_, pattern) => match value {
ConValue::Ref(value) => append_sub(env, pattern, Rc::unwrap_or_clone(value)),
_ => Err(Error::NotAssignable),
},
Pattern::Tuple(patterns) => match value {
ConValue::Tuple(values) => {
if patterns.len() != values.len() {
return Err(Error::OobIndex(patterns.len(), values.len()));
};
for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) {
append_sub(env, pat, value)?;
}
Ok(())
}
_ => Err(Error::NotAssignable),
},
Pattern::Array(patterns) => match value {
ConValue::Array(values) => {
if patterns.len() != values.len() {
return Err(Error::OobIndex(patterns.len(), values.len()));
};
for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) {
append_sub(env, pat, value)?;
}
Ok(())
}
_ => Err(Error::NotAssignable),
},
Pattern::Struct(_path, patterns) => {
let ConValue::Struct(parts) = value else {
return Err(Error::TypeError);
};
let (_, mut values) = *parts;
if values.len() != patterns.len() {
return Err(Error::TypeError);
}
for (name, pat) in patterns {
let [.., PathPart::Ident(index)] = name.parts.as_slice() else {
Err(Error::TypeError)?
};
let value = values.remove(index).ok_or(Error::TypeError)?;
match pat {
Some(pat) => append_sub(env, pat, value)?,
None => {
env.insert(name, value);
}
}
}
Ok(())
}
}
}
/// Constructs a substitution from a pattern and a value
pub fn pattern_substitution(
pat: &Pattern,
value: ConValue,
) -> IResult<HashMap<&Path, ConValue>> {
let mut substitution = HashMap::new();
append_sub(&mut substitution, pat, value)?;
Ok(substitution)
}
pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> {
let mut substitution = HashMap::new();
append_sub(&mut substitution, pat, value)
.map_err(|_| Error::PatFailed(pat.clone().into()))?;
for (path, value) in substitution {
assign_path(env, path, value)?;
}
Ok(())
}
pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> {
if let Ok(pat) = Pattern::try_from(pat.clone()) {
return pat_assign(env, &pat, value);
}
match pat {
ExprKind::Member(member) => *addrof_member(env, member)? = value,
ExprKind::Index(index) => *addrof_index(env, index)? = value,
_ => Err(Error::NotAssignable)?,
}
Ok(())
}
fn assign_path(env: &mut Environment, path: &Path, value: ConValue) -> IResult<()> {
let Ok(addr) = addrof_path(env, &path.parts) else {
eprintln!("Cannot assign {value} to path {path}");
return Err(Error::NotAssignable);
};
*addr = Some(value);
Ok(())
}
pub(super) fn addrof<'e>(
env: &'e mut Environment,
pat: &ExprKind,
) -> IResult<&'e mut ConValue> {
match pat {
ExprKind::Path(path) => addrof_path(env, &path.parts)?
.as_mut()
.ok_or(Error::NotInitialized("".into())),
ExprKind::Member(member) => addrof_member(env, member),
ExprKind::Index(index) => addrof_index(env, index),
ExprKind::Group(Group { expr }) => addrof(env, expr),
ExprKind::AddrOf(AddrOf { mutable: Mutability::Mut, expr }) => addrof(env, expr),
_ => Err(Error::TypeError),
}
}
pub fn addrof_path<'e>(
env: &'e mut Environment,
path: &[PathPart],
) -> IResult<&'e mut Option<ConValue>> {
match path {
[PathPart::Ident(name)] => env.get_mut(*name),
[PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? {
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest),
_ => Err(Error::NotIndexable),
},
_ => Err(Error::NotAssignable),
}
}
fn addrof_member<'e>(env: &'e mut Environment, member: &Member) -> IResult<&'e mut ConValue> {
let Member { head, kind } = member;
let ExprKind::Path(path) = head.as_ref() else {
return Err(Error::TypeError);
};
let slot = addrof_path(env, &path.parts)?
.as_mut()
.ok_or(Error::NotAssignable)?;
Ok(match (slot, kind) {
(ConValue::Struct(s), MemberKind::Struct(id)) => {
s.1.get_mut(id).ok_or(Error::NotDefined(*id))?
}
(ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => t
.get_mut(*id as usize)
.ok_or_else(|| Error::NotDefined(id.to_string().into()))?,
_ => Err(Error::TypeError)?,
})
}
fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> {
let Index { head, indices } = index;
let indices = indices
.iter()
.map(|index| index.interpret(env))
.collect::<IResult<Vec<_>>>()?;
let mut head = addrof(env, head)?;
for index in indices {
head = match (head, index) {
(ConValue::Array(a), ConValue::Int(i)) => {
let a_len = a.len();
a.get_mut(i as usize)
.ok_or(Error::OobIndex(i as usize, a_len))?
}
_ => Err(Error::NotIndexable)?,
}
}
Ok(head)
}
pub fn addrof_path_within_namespace<'e>(
env: &'e mut Namespace,
path: &[PathPart],
) -> IResult<&'e mut Option<ConValue>> {
match path {
[] => Err(Error::NotAssignable),
[PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)),
[PathPart::Ident(name), rest @ ..] => {
match env.get_mut(name).ok_or(Error::NotDefined(*name))? {
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest),
_ => Err(Error::NotIndexable),
}
}
[PathPart::SelfKw, rest @ ..] => addrof_path_within_namespace(env, rest),
[PathPart::SelfTy, ..] => todo!("calc_address for `Self`"),
[PathPart::SuperKw, ..] => todo!("calc_address for `super`"),
} }
_ => Err(Error::NotAssignable),
} }
} }
@@ -188,17 +533,7 @@ impl Interpret for Assign {
let (head, tail) = parts.borrow(); let (head, tail) = parts.borrow();
let init = tail.interpret(env)?; let init = tail.interpret(env)?;
// Resolve the head pattern // Resolve the head pattern
let target = evaluate_place_expr(env, head)?; assignment::assign(env, head, init).map(|_| ConValue::Empty)
use std::mem::discriminant as variant;
// runtime typecheck
match target.0 {
Some(value) if variant(value) == variant(&init) => {
*value = init;
}
value @ None => *value = Some(init),
_ => Err(Error::TypeError)?,
}
Ok(ConValue::Empty)
} }
} }
impl Interpret for Modify { impl Interpret for Modify {
@@ -208,10 +543,7 @@ impl Interpret for Modify {
// Get the initializer and the tail // Get the initializer and the tail
let init = tail.interpret(env)?; let init = tail.interpret(env)?;
// Resolve the head pattern // Resolve the head pattern
let target = evaluate_place_expr(env, head)?; let target = assignment::addrof(env, head)?;
let (Some(target), _) = target else {
return Err(Error::NotInitialized(target.1));
};
match op { match op {
ModifyKind::Add => target.add_assign(init), ModifyKind::Add => target.add_assign(init),
@@ -321,12 +653,28 @@ impl Interpret for Binary {
impl Interpret for Unary { impl Interpret for Unary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Unary { kind, tail } = self; let Unary { kind, tail } = self;
let operand = tail.interpret(env)?;
match kind { match kind {
UnaryKind::Deref => env.call("deref".into(), &[operand]), UnaryKind::Loop => loop {
UnaryKind::Neg => env.call("neg".into(), &[operand]), match tail.interpret(env) {
UnaryKind::Not => env.call("not".into(), &[operand]), Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
},
UnaryKind::Deref => {
let operand = tail.interpret(env)?;
env.call("deref".into(), &[operand])
}
UnaryKind::Neg => {
let operand = tail.interpret(env)?;
env.call("neg".into(), &[operand])
}
UnaryKind::Not => {
let operand = tail.interpret(env)?;
env.call("not".into(), &[operand])
}
UnaryKind::At => { UnaryKind::At => {
let operand = tail.interpret(env)?;
println!("{operand}"); println!("{operand}");
Ok(operand) Ok(operand)
} }
@@ -334,6 +682,53 @@ impl Interpret for Unary {
} }
} }
} }
fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
let value = match value {
ConValue::Empty => 0,
ConValue::Int(i) => i as _,
ConValue::Bool(b) => b as _,
ConValue::Char(c) => c as _,
ConValue::Ref(v) => return cast((*v).clone(), ty),
// TODO: This, better
ConValue::Float(_) if ty.starts_with('f') => return Ok(value),
ConValue::Float(f) => f as _,
_ => Err(Error::TypeError)?,
};
Ok(match &*ty {
"u8" => ConValue::Int(value as u8 as _),
"i8" => ConValue::Int(value as i8 as _),
"u16" => ConValue::Int(value as u16 as _),
"i16" => ConValue::Int(value as i16 as _),
"u32" => ConValue::Int(value as u32 as _),
"i32" => ConValue::Int(value as i32 as _),
"u64" => ConValue::Int(value),
"i64" => ConValue::Int(value),
"f32" => ConValue::Float(value as f32 as _),
"f64" => ConValue::Float(value as f64 as _),
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
"bool" => ConValue::Bool(value < 0),
_ => Err(Error::NotDefined(ty))?,
})
}
impl Interpret for Cast {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Cast { head, ty } = self;
let value = head.interpret(env)?;
if TyKind::Empty == ty.kind {
return Ok(ConValue::Empty);
};
let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else {
Err(Error::TypeError)?
};
match parts.as_slice() {
[PathPart::Ident(ty)] => cast(value, *ty),
_ => Err(Error::TypeError),
}
}
}
impl Interpret for Member { impl Interpret for Member {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Member { head, kind } = self; let Member { head, kind } = self;
@@ -343,6 +738,20 @@ impl Interpret for Member {
.get(*id as usize) .get(*id as usize)
.cloned() .cloned()
.ok_or(Error::OobIndex(*id as usize, v.len())), .ok_or(Error::OobIndex(*id as usize, v.len())),
(ConValue::Struct(parts), MemberKind::Struct(name)) => {
parts.1.get(name).cloned().ok_or(Error::NotDefined(*name))
}
(ConValue::Struct(parts), MemberKind::Call(name, args)) => {
let mut values = vec![];
for arg in &args.exprs {
values.push(arg.interpret(env)?);
}
(parts.1)
.get(name)
.cloned()
.ok_or(Error::NotDefined(*name))?
.call(env, &values)
}
(head, MemberKind::Call(name, args)) => { (head, MemberKind::Call(name, args)) => {
let mut values = vec![head]; let mut values = vec![head];
for arg in &args.exprs { for arg in &args.exprs {
@@ -366,22 +775,37 @@ impl Interpret for Index {
} }
impl Interpret for Structor { impl Interpret for Structor {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("struct construction in {env}") let Self { to: Path { absolute: _, parts }, init } = self;
use std::collections::HashMap;
let name = match parts.last() {
Some(PathPart::Ident(name)) => *name,
Some(PathPart::SelfKw) => "self".into(),
Some(PathPart::SelfTy) => "Self".into(),
Some(PathPart::SuperKw) => "super".into(),
None => "".into(),
};
let mut map = HashMap::new();
for Fielder { name, init } in init {
let value = match init {
Some(init) => init.interpret(env)?,
None => env.get(*name)?,
};
map.insert(*name, value);
}
Ok(ConValue::Struct(Box::new((name, map))))
} }
} }
impl Interpret for Path { impl Interpret for Path {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { absolute: _, parts } = self; let Self { absolute: _, parts } = self;
if parts.len() == 1 { assignment::addrof_path(env, parts)
match parts.last().expect("parts should not be empty") { .cloned()
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"), .transpose()
PathPart::SelfTy => todo!("Path navigation to Self"), .ok_or_else(|| Error::NotInitialized(format!("{self}").into()))?
PathPart::Ident(name) => env.get(*name),
}
} else {
todo!("Path navigation!")
}
} }
} }
impl Interpret for Literal { impl Interpret for Literal {
@@ -390,7 +814,7 @@ impl Interpret for Literal {
Literal::String(value) => ConValue::from(value.as_str()), Literal::String(value) => ConValue::from(value.as_str()),
Literal::Char(value) => ConValue::Char(*value), Literal::Char(value) => ConValue::Char(*value),
Literal::Bool(value) => ConValue::Bool(*value), Literal::Bool(value) => ConValue::Bool(*value),
// Literal::Float(value) => todo!("Float values in interpreter: {value:?}"), Literal::Float(value) => ConValue::Float(f64::from_bits(*value)),
Literal::Int(value) => ConValue::Int(*value as _), Literal::Int(value) => ConValue::Int(*value as _),
}) })
} }
@@ -418,13 +842,9 @@ impl Interpret for ArrayRep {
} }
impl Interpret for AddrOf { impl Interpret for AddrOf {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { count: _, mutable: _, expr } = self; let Self { mutable: _, expr } = self;
match expr.as_ref() { match expr.as_ref() {
ExprKind::Index(_) => todo!("AddrOf array index"), ExprKind::Index(_) => todo!("AddrOf array index"),
// ExprKind::Path(Path { absolute: false, parts }) => match parts.as_slice() {
// [PathPart::Ident(id)] => env.get_ref(id),
// _ => todo!("Path traversal in addrof"),
// },
ExprKind::Path(_) => todo!("Path traversal in addrof"), ExprKind::Path(_) => todo!("Path traversal in addrof"),
_ => Ok(ConValue::Ref(Rc::new(expr.interpret(env)?))), _ => Ok(ConValue::Ref(Rc::new(expr.interpret(env)?))),
} }
@@ -461,18 +881,6 @@ impl Interpret for Tuple {
)) ))
} }
} }
impl Interpret for Loop {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self;
loop {
match body.interpret(env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
}
}
}
impl Interpret for While { impl Interpret for While {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
@@ -502,16 +910,19 @@ impl Interpret for If {
impl Interpret for For { impl Interpret for For {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { bind: name, cond, pass, fail } = self; let Self { bind: name, cond, pass, fail } = self;
let cond = cond.interpret(env)?;
// TODO: A better iterator model // TODO: A better iterator model
let mut bounds = match cond.interpret(env)? { let mut bounds: Box<dyn Iterator<Item = ConValue>> = match &cond {
ConValue::RangeExc(a, b) => a..=b, &ConValue::RangeExc(a, b) => Box::new((a..b).map(ConValue::Int)),
ConValue::RangeInc(a, b) => a..=b, &ConValue::RangeInc(a, b) => Box::new((a..=b).map(ConValue::Int)),
ConValue::Array(a) => Box::new(a.iter().cloned()),
ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError)?,
}; };
loop { loop {
let mut env = env.frame("loop variable"); let mut env = env.frame("loop variable");
if let Some(loop_var) = bounds.next() { if let Some(loop_var) = bounds.next() {
env.insert(*name, Some(loop_var.into())); env.insert(*name, Some(loop_var));
match pass.interpret(&mut env) { match pass.interpret(&mut env) {
Err(Error::Break(value)) => return Ok(value), Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue, Err(Error::Continue) => continue,
@@ -532,11 +943,6 @@ impl Interpret for Else {
} }
} }
} }
impl Interpret for Continue {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
Err(Error::Continue)
}
}
impl Interpret for Return { impl Interpret for Return {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self; let Self { body } = self;

View File

@@ -17,616 +17,17 @@ pub trait Callable: std::fmt::Debug {
fn name(&self) -> Sym; fn name(&self) -> Sym;
} }
/// [BuiltIn]s are [Callable]s with bespoke definitions pub mod convalue;
pub trait BuiltIn: std::fmt::Debug + Callable {
fn description(&self) -> &str;
}
pub mod convalue {
//! Values in the dynamically typed AST interpreter.
//!
//! The most permanent fix is a temporary one.
use cl_ast::Sym;
use super::{
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Environment,
};
use std::{ops::*, rc::Rc};
type Integer = isize;
/// A Conlang value stores data in the interpreter
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string
String(Sym),
/// A reference
Ref(Rc<ConValue>),
/// An Array
Array(Rc<[ConValue]>),
/// A tuple
Tuple(Rc<[ConValue]>),
/// An exclusive range
RangeExc(Integer, Integer),
/// An inclusive range
RangeInc(Integer, Integer),
/// A callable thing
Function(Function),
/// A built-in function
BuiltIn(&'static dyn BuiltIn),
}
impl ConValue {
/// Gets whether the current value is true or false
pub fn truthy(&self) -> IResult<bool> {
match self {
ConValue::Bool(v) => Ok(*v),
_ => Err(Error::TypeError)?,
}
}
pub fn range_exc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeExc(a, b.saturating_sub(1)))
}
pub fn range_inc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeInc(a, b))
}
pub fn index(&self, index: &Self) -> IResult<ConValue> {
let Self::Int(index) = index else {
Err(Error::TypeError)?
};
let Self::Array(arr) = self else {
Err(Error::TypeError)?
};
arr.get(*index as usize)
.cloned()
.ok_or(Error::OobIndex(*index as usize, arr.len()))
}
cmp! {
lt: false, <;
lt_eq: true, <=;
eq: true, ==;
neq: false, !=;
gt_eq: true, >=;
gt: false, >;
}
assign! {
add_assign: +;
bitand_assign: &;
bitor_assign: |;
bitxor_assign: ^;
div_assign: /;
mul_assign: *;
rem_assign: %;
shl_assign: <<;
shr_assign: >>;
sub_assign: -;
}
}
impl Callable for ConValue {
fn name(&self) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::BuiltIn(func) => func.name(),
_ => "".into(),
}
}
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
match self {
Self::Function(func) => func.call(interpreter, args),
Self::BuiltIn(func) => func.call(interpreter, args),
_ => Err(Error::NotCallable(self.clone())),
}
}
}
/// Templates comparison functions for [ConValue]
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
/// TODO: Remove when functions are implemented:
/// Desugar into function calls
pub fn $fn(&self, other: &Self) -> IResult<Self> {
match (self, other) {
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
_ => Err(Error::TypeError)
}
}
)*}
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
pub fn $fn(&mut self, other: Self) -> IResult<()> {
*self = (std::mem::take(self) $op other)?;
Ok(())
}
)*}
/// Implements [From] for an enum with 1-tuple variants
macro from ($($T:ty => $v:expr),*$(,)?) {
$(impl From<$T> for ConValue {
fn from(value: $T) -> Self { $v(value.into()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::String(*value)
}
}
from! {
Integer => ConValue::Int,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::String,
&str => ConValue::String,
String => ConValue::String,
Rc<str> => ConValue::String,
Function => ConValue::Function,
Vec<ConValue> => ConValue::Tuple,
&'static dyn BuiltIn => ConValue::BuiltIn,
}
impl From<()> for ConValue {
fn from(_: ()) -> Self {
Self::Empty
}
}
impl From<&[ConValue]> for ConValue {
fn from(value: &[ConValue]) -> Self {
match value.len() {
0 => Self::Empty,
1 => value[0].clone(),
_ => Self::Tuple(value.into()),
}
}
}
/// Implements binary [std::ops] traits for [ConValue]
///
/// TODO: Desugar operators into function calls
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
$(impl $trait for ConValue {
type Output = IResult<Self>;
/// TODO: Desugar operators into function calls
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
})*
}
ops! {
Add: add = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>().into())
}
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} / {b}"); a
})),
_ => Err(Error::TypeError)?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
_ => Err(Error::TypeError)?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} % {b}"); a
})),
_ => Err(Error::TypeError)?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError)?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError)?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
_ => Err(Error::TypeError)?
]
}
impl std::fmt::Display for ConValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConValue::Empty => "Empty".fmt(f),
ConValue::Int(v) => v.fmt(f),
ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => v.fmt(f),
ConValue::String(v) => v.fmt(f),
ConValue::Ref(v) => write!(f, "&{v}"),
ConValue::Array(array) => {
'['.fmt(f)?;
for (idx, element) in array.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
']'.fmt(f)
}
ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1),
ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"),
ConValue::Tuple(tuple) => {
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::Function(func) => {
write!(f, "{}", func.decl())
}
ConValue::BuiltIn(func) => {
write!(f, "{}", func.description())
}
}
}
}
}
pub mod interpret; pub mod interpret;
pub mod function { pub mod function;
//! Represents a block of code which lives inside the Interpreter
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Param, Sym};
use std::rc::Rc;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Rc<FnDecl>,
// /// Stores the enclosing scope of the function
// env: Box<Environment>,
}
impl Function {
pub fn new(decl: &FnDecl) -> Self {
Self { decl: decl.clone().into() }
}
pub fn decl(&self) -> &FnDecl {
&self.decl
}
}
impl Callable for Function {
fn name(&self) -> Sym {
let FnDecl { name, .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
// Check arg mapping
if args.len() != bind.len() {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(*name));
};
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name }, value) in bind.iter().zip(args) {
frame.insert(*name, Some(value.clone()));
}
match body.interpret(&mut frame) {
Err(Error::Return(value)) => Ok(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
result => result,
}
}
}
}
pub mod builtin; pub mod builtin;
pub mod env { pub mod env;
//! Lexical and non-lexical scoping for variables
use super::{
builtin::{BINARY, MISC, RANGE, UNARY},
convalue::ConValue,
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
};
type StackFrame = HashMap<Sym, Option<ConValue>>; pub mod error;
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
frames: Vec<(StackFrame, &'static str)>,
}
impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (frame, name) in self.frames.iter().rev() {
writeln!(f, "--- {name} ---")?;
for (var, val) in frame {
write!(f, "{var}: ")?;
match val {
Some(value) => writeln!(f, "\t{value}"),
None => writeln!(f, "<undefined>"),
}?
}
}
Ok(())
}
}
impl Default for Environment {
fn default() -> Self {
Self {
frames: vec![
(to_hashmap(RANGE), "range ops"),
(to_hashmap(UNARY), "unary ops"),
(to_hashmap(BINARY), "binary ops"),
(to_hashmap(MISC), "builtins"),
(HashMap::new(), "globals"),
],
}
}
}
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<Sym, Option<ConValue>> {
from.iter().map(|&v| (v.name(), Some(v.into()))).collect()
}
impl Environment {
pub fn new() -> Self {
Self::default()
}
/// Creates an [Environment] with no [builtins](super::builtin)
pub fn no_builtins(name: &'static str) -> Self {
Self { frames: vec![(Default::default(), name)] }
}
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
node.interpret(self)
}
/// Calls a function inside the interpreter's scope,
/// and returns the result
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
// FIXME: Clone to satisfy the borrow checker
let function = self.get(name)?.clone();
function.call(self, args)
}
/// Enters a nested scope, returning a [`Frame`] stack-guard.
///
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn frame(&mut self, name: &'static str) -> Frame {
Frame::new(self, name)
}
/// Resolves a variable mutably.
///
/// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, id: Sym) -> IResult<&mut Option<ConValue>> {
for (frame, _) in self.frames.iter_mut().rev() {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
Err(Error::NotDefined(id))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotDefined(id))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id, value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl;
let (name, function) = (name, Some(Function::new(decl).into()));
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(*name, function);
}
}
}
/// Functions which aid in the implementation of [`Frame`]
impl Environment {
/// Enters a scope, creating a new namespace for variables
fn enter(&mut self, name: &'static str) -> &mut Self {
self.frames.push((Default::default(), name));
self
}
/// Exits the scope, destroying all local variables and
/// returning the outer scope, if there is one
fn exit(&mut self) -> &mut Self {
if self.frames.len() > 2 {
self.frames.pop();
}
self
}
}
/// Represents a stack frame
#[derive(Debug)]
pub struct Frame<'scope> {
scope: &'scope mut Environment,
}
impl<'scope> Frame<'scope> {
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
Self { scope: scope.enter(name) }
}
}
impl<'scope> Deref for Frame<'scope> {
type Target = Environment;
fn deref(&self) -> &Self::Target {
self.scope
}
}
impl<'scope> DerefMut for Frame<'scope> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.scope
}
}
impl<'scope> Drop for Frame<'scope> {
fn drop(&mut self) {
self.scope.exit();
}
}
}
pub mod error {
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::Sym;
use super::convalue::ConValue;
pub type IResult<T> = Result<T, Error>;
/// Represents any error thrown by the [Environment](super::Environment)
#[derive(Clone, Debug)]
pub enum Error {
/// Propagate a Return value
Return(ConValue),
/// Propagate a Break value
Break(ConValue),
/// Break propagated across function bounds
BadBreak(ConValue),
/// Continue to the next iteration of a loop
Continue,
/// Underflowed the stack
StackUnderflow,
/// Exited the last scope
ScopeExit,
/// Type incompatibility
// TODO: store the type information in this error
TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
/// A value could not be indexed
NotIndexable,
/// An array index went out of bounds
OobIndex(usize, usize),
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(Sym),
/// A name was defined but not initialized
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber {
want: usize,
got: usize,
},
Outlined(Sym),
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Return(value) => write!(f, "return {value}"),
Error::Break(value) => write!(f, "break {value}"),
Error::BadBreak(value) => write!(f, "rogue break: {value}"),
Error::Continue => "continue".fmt(f),
Error::StackUnderflow => "Stack underflow".fmt(f),
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
Error::TypeError => "Incompatible types".fmt(f),
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
Error::NotIndexable => {
write!(f, "expression cannot be indexed")
}
Error::OobIndex(idx, len) => {
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
}
Error::NotAssignable => {
write!(f, "expression is not assignable")
}
Error::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?")
}
Error::NotInitialized(value) => {
write!(f, "{value} bound, but not initialized")
}
Error::NotCallable(value) => {
write!(f, "{value} is not callable.")
}
Error::ArgNumber { want, got } => {
write!(
f,
"Expected {want} argument{}, got {got}",
if *want == 1 { "" } else { "s" }
)
}
Error::Outlined(name) => {
write!(f, "Module {name} specified, but not imported.")
}
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@@ -1,5 +1,5 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use crate::{env::Environment, convalue::ConValue, Interpret}; use crate::{convalue::ConValue, env::Environment, Interpret};
use cl_ast::*; use cl_ast::*;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
@@ -48,6 +48,7 @@ mod macros {
//! ``` //! ```
#![allow(unused_macros)] #![allow(unused_macros)]
use crate::IResult; use crate::IResult;
use cl_parser::parser::Parse;
use super::*; use super::*;
@@ -63,14 +64,14 @@ mod macros {
/// ///
/// Returns a `Result<`[`File`]`, ParseError>` /// Returns a `Result<`[`File`]`, ParseError>`
pub macro file($($t:tt)*) { pub macro file($($t:tt)*) {
Parser::new(Lexer::new(stringify!( $($t)* ))).file() File::parse(&mut Parser::new(Lexer::new(stringify!( $($t)* ))))
} }
/// Stringifies, lexes, and parses everything you give to it /// Stringifies, lexes, and parses everything you give to it
/// ///
/// Returns a `Result<`[`Block`]`, ParseError>` /// Returns a `Result<`[`Block`]`, ParseError>`
pub macro block($($t:tt)*) { pub macro block($($t:tt)*) {
Parser::new(Lexer::new(stringify!({ $($t)* }))).block() Block::parse(&mut Parser::new(Lexer::new(stringify!({ $($t)* }))))
} }
/// Evaluates a block of code in the given environment /// Evaluates a block of code in the given environment
@@ -177,6 +178,45 @@ mod let_declarations {
env_eq!(env.x, 10); env_eq!(env.x, 10);
env_eq!(env.y, 10); env_eq!(env.y, 10);
} }
#[test]
fn let_destructuring_tuple() {
let mut env = Environment::new();
assert_eval!(env,
let (x, y) = (10, 20);
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_array() {
let mut env = Environment::new();
assert_eval!(env,
let [x, y] = [10, 20];
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_nested() {
let mut env = Environment::new();
assert_eval!(env,
let (x, [one, two, three], (a, b, c))
= ('x', [1, 2, 3], ('a', 'b', 'c'));
);
env_eq!(env.x, 'x');
env_eq!(env.one, 1);
env_eq!(env.two, 2);
env_eq!(env.three, 3);
env_eq!(env.a, 'a');
env_eq!(env.b, 'b');
env_eq!(env.c, 'c');
}
} }
mod fn_declarations { mod fn_declarations {
@@ -187,7 +227,7 @@ mod fn_declarations {
assert_eval!(env, fn empty_fn() {}); assert_eval!(env, fn empty_fn() {});
// TODO: true equality for functions // TODO: true equality for functions
assert_eq!( assert_eq!(
"fn empty_fn () {\n \n}", "fn empty_fn () {}",
format!( format!(
"{}", "{}",
env.get("empty_fn".into()) env.get("empty_fn".into())
@@ -436,16 +476,17 @@ mod operators {
env_eq!(env.y, 10); env_eq!(env.y, 10);
env_eq!(env.z, 10); env_eq!(env.z, 10);
} }
#[test] // Test is disabled, since new assignment system intentionally does not care.
#[should_panic] // #[test]
fn assignment_accounts_for_type() { // #[should_panic]
let mut env = Default::default(); // fn assignment_accounts_for_type() {
assert_eval!(env, // let mut env = Default::default();
let x = "a string"; // assert_eval!(env,
let y = 0xdeadbeef; // let x = "a string";
y = x; // should crash: type error // let y = 0xdeadbeef;
); // y = x; // should crash: type error
} // );
// }
#[test] #[test]
fn precedence() { fn precedence() {
let mut env = Default::default(); let mut env = Default::default();
@@ -468,6 +509,56 @@ mod operators {
} }
} }
mod control_flow {
use super::*;
#[test]
fn if_evaluates_pass_block_on_true() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if true { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "pass");
}
#[test]
fn if_evaluates_fail_block_on_false() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if false { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "fail");
}
#[test]
fn match_evaluates_in_order() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
'\u{1f988}' => true,
_ => false,
};
);
env_eq!(env.passed, true);
}
#[test]
fn match_sinkoles_underscore_patterns() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
_ => true,
'\u{1f988}' => false,
};
);
env_eq!(env.passed, true);
}
//TODO: test other control flow constructs like loops, while-else, etc.
}
#[allow(dead_code)] #[allow(dead_code)]
fn test_template() { fn test_template() {
let mut env = Default::default(); let mut env = Default::default();

View File

@@ -23,7 +23,7 @@ pub mod lexer_iter {
pub struct LexerIter<'t> { pub struct LexerIter<'t> {
lexer: Lexer<'t>, lexer: Lexer<'t>,
} }
impl<'t> Iterator for LexerIter<'t> { impl Iterator for LexerIter<'_> {
type Item = LResult<Token>; type Item = LResult<Token>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.lexer.scan() { match self.lexer.scan() {
@@ -97,33 +97,33 @@ impl<'t> Lexer<'t> {
/// Scans through the text, searching for the next [Token] /// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> { pub fn scan(&mut self) -> LResult<Token> {
match self.skip_whitespace().peek()? { match self.skip_whitespace().peek()? {
'{' => self.consume()?.produce_op(Punct::LCurly), '{' => self.consume()?.produce_op(Kind::LCurly),
'}' => self.consume()?.produce_op(Punct::RCurly), '}' => self.consume()?.produce_op(Kind::RCurly),
'[' => self.consume()?.produce_op(Punct::LBrack), '[' => self.consume()?.produce_op(Kind::LBrack),
']' => self.consume()?.produce_op(Punct::RBrack), ']' => self.consume()?.produce_op(Kind::RBrack),
'(' => self.consume()?.produce_op(Punct::LParen), '(' => self.consume()?.produce_op(Kind::LParen),
')' => self.consume()?.produce_op(Punct::RParen), ')' => self.consume()?.produce_op(Kind::RParen),
'&' => self.consume()?.amp(), '&' => self.consume()?.amp(),
'@' => self.consume()?.produce_op(Punct::At), '@' => self.consume()?.produce_op(Kind::At),
'\\' => self.consume()?.produce_op(Punct::Backslash), '\\' => self.consume()?.produce_op(Kind::Backslash),
'!' => self.consume()?.bang(), '!' => self.consume()?.bang(),
'|' => self.consume()?.bar(), '|' => self.consume()?.bar(),
':' => self.consume()?.colon(), ':' => self.consume()?.colon(),
',' => self.consume()?.produce_op(Punct::Comma), ',' => self.consume()?.produce_op(Kind::Comma),
'.' => self.consume()?.dot(), '.' => self.consume()?.dot(),
'=' => self.consume()?.equal(), '=' => self.consume()?.equal(),
'`' => self.consume()?.produce_op(Punct::Grave), '`' => self.consume()?.produce_op(Kind::Grave),
'>' => self.consume()?.greater(), '>' => self.consume()?.greater(),
'#' => self.consume()?.hash(), '#' => self.consume()?.hash(),
'<' => self.consume()?.less(), '<' => self.consume()?.less(),
'-' => self.consume()?.minus(), '-' => self.consume()?.minus(),
'+' => self.consume()?.plus(), '+' => self.consume()?.plus(),
'?' => self.consume()?.produce_op(Punct::Question), '?' => self.consume()?.produce_op(Kind::Question),
'%' => self.consume()?.rem(), '%' => self.consume()?.rem(),
';' => self.consume()?.produce_op(Punct::Semi), ';' => self.consume()?.produce_op(Kind::Semi),
'/' => self.consume()?.slash(), '/' => self.consume()?.slash(),
'*' => self.consume()?.star(), '*' => self.consume()?.star(),
'~' => self.consume()?.produce_op(Punct::Tilde), '~' => self.consume()?.produce_op(Kind::Tilde),
'^' => self.consume()?.xor(), '^' => self.consume()?.xor(),
'0' => self.consume()?.int_with_base(), '0' => self.consume()?.int_with_base(),
'1'..='9' => self.digits::<10>(), '1'..='9' => self.digits::<10>(),
@@ -157,14 +157,14 @@ impl<'t> Lexer<'t> {
.copied() .copied()
.ok_or(Error::end_of_file(self.line(), self.col())) .ok_or(Error::end_of_file(self.line(), self.col()))
} }
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> { fn produce(&mut self, kind: Kind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.start_loc; let loc = self.start_loc;
self.start_loc = self.current_loc; self.start_loc = self.current_loc;
self.start = self.current; self.start = self.current;
Ok(Token::new(kind, data, loc.0, loc.1)) Ok(Token::new(kind, data, loc.0, loc.1))
} }
fn produce_op(&mut self, kind: Punct) -> LResult<Token> { fn produce_op(&mut self, kind: Kind) -> LResult<Token> {
self.produce(TokenKind::Punct(kind), ()) self.produce(kind, ())
} }
fn skip_whitespace(&mut self) -> &mut Self { fn skip_whitespace(&mut self) -> &mut Self {
while let Ok(c) = self.peek() { while let Ok(c) = self.peek() {
@@ -192,145 +192,154 @@ impl<'t> Lexer<'t> {
} }
} }
/// Digraphs and trigraphs /// Digraphs and trigraphs
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn amp(&mut self) -> LResult<Token> { fn amp(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp), Ok('&') => self.consume()?.produce_op(Kind::AmpAmp),
Ok('=') => self.consume()?.produce_op(Punct::AmpEq), Ok('=') => self.consume()?.produce_op(Kind::AmpEq),
_ => self.produce_op(Punct::Amp), _ => self.produce_op(Kind::Amp),
} }
} }
fn bang(&mut self) -> LResult<Token> { fn bang(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::BangBang), Ok('!') => self.consume()?.produce_op(Kind::BangBang),
Ok('=') => self.consume()?.produce_op(Punct::BangEq), Ok('=') => self.consume()?.produce_op(Kind::BangEq),
_ => self.produce_op(Punct::Bang), _ => self.produce_op(Kind::Bang),
} }
} }
fn bar(&mut self) -> LResult<Token> { fn bar(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('|') => self.consume()?.produce_op(Punct::BarBar), Ok('|') => self.consume()?.produce_op(Kind::BarBar),
Ok('=') => self.consume()?.produce_op(Punct::BarEq), Ok('=') => self.consume()?.produce_op(Kind::BarEq),
_ => self.produce_op(Punct::Bar), _ => self.produce_op(Kind::Bar),
} }
} }
fn colon(&mut self) -> LResult<Token> { fn colon(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok(':') => self.consume()?.produce_op(Punct::ColonColon), Ok(':') => self.consume()?.produce_op(Kind::ColonColon),
_ => self.produce_op(Punct::Colon), _ => self.produce_op(Kind::Colon),
} }
} }
fn dot(&mut self) -> LResult<Token> { fn dot(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('.') => { Ok('.') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::DotDotEq) self.consume()?.produce_op(Kind::DotDotEq)
} else { } else {
self.produce_op(Punct::DotDot) self.produce_op(Kind::DotDot)
} }
} }
_ => self.produce_op(Punct::Dot), _ => self.produce_op(Kind::Dot),
} }
} }
fn equal(&mut self) -> LResult<Token> { fn equal(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::EqEq), Ok('=') => self.consume()?.produce_op(Kind::EqEq),
Ok('>') => self.consume()?.produce_op(Punct::FatArrow), Ok('>') => self.consume()?.produce_op(Kind::FatArrow),
_ => self.produce_op(Punct::Eq), _ => self.produce_op(Kind::Eq),
} }
} }
fn greater(&mut self) -> LResult<Token> { fn greater(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::GtEq), Ok('=') => self.consume()?.produce_op(Kind::GtEq),
Ok('>') => { Ok('>') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::GtGtEq) self.consume()?.produce_op(Kind::GtGtEq)
} else { } else {
self.produce_op(Punct::GtGt) self.produce_op(Kind::GtGt)
} }
} }
_ => self.produce_op(Punct::Gt), _ => self.produce_op(Kind::Gt),
} }
} }
fn hash(&mut self) -> LResult<Token> { fn hash(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::HashBang), Ok('!') => self.consume()?.hashbang(),
_ => self.produce_op(Punct::Hash), _ => self.produce_op(Kind::Hash),
}
}
fn hashbang(&mut self) -> LResult<Token> {
match self.peek() {
Ok('/' | '\'') => self.line_comment(),
_ => self.produce_op(Kind::HashBang),
} }
} }
fn less(&mut self) -> LResult<Token> { fn less(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::LtEq), Ok('=') => self.consume()?.produce_op(Kind::LtEq),
Ok('<') => { Ok('<') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::LtLtEq) self.consume()?.produce_op(Kind::LtLtEq)
} else { } else {
self.produce_op(Punct::LtLt) self.produce_op(Kind::LtLt)
} }
} }
_ => self.produce_op(Punct::Lt), _ => self.produce_op(Kind::Lt),
} }
} }
fn minus(&mut self) -> LResult<Token> { fn minus(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::MinusEq), Ok('=') => self.consume()?.produce_op(Kind::MinusEq),
Ok('>') => self.consume()?.produce_op(Punct::Arrow), Ok('>') => self.consume()?.produce_op(Kind::Arrow),
_ => self.produce_op(Punct::Minus), _ => self.produce_op(Kind::Minus),
} }
} }
fn plus(&mut self) -> LResult<Token> { fn plus(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::PlusEq), Ok('=') => self.consume()?.produce_op(Kind::PlusEq),
_ => self.produce_op(Punct::Plus), _ => self.produce_op(Kind::Plus),
} }
} }
fn rem(&mut self) -> LResult<Token> { fn rem(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::RemEq), Ok('=') => self.consume()?.produce_op(Kind::RemEq),
_ => self.produce_op(Punct::Rem), _ => self.produce_op(Kind::Rem),
} }
} }
fn slash(&mut self) -> LResult<Token> { fn slash(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::SlashEq), Ok('=') => self.consume()?.produce_op(Kind::SlashEq),
Ok('/') => self.consume()?.line_comment(), Ok('/') => self.consume()?.line_comment(),
Ok('*') => self.consume()?.block_comment(), Ok('*') => self.consume()?.block_comment(),
_ => self.produce_op(Punct::Slash), _ => self.produce_op(Kind::Slash),
} }
} }
fn star(&mut self) -> LResult<Token> { fn star(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::StarEq), Ok('=') => self.consume()?.produce_op(Kind::StarEq),
_ => self.produce_op(Punct::Star), _ => self.produce_op(Kind::Star),
} }
} }
fn xor(&mut self) -> LResult<Token> { fn xor(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::XorEq), Ok('=') => self.consume()?.produce_op(Kind::XorEq),
Ok('^') => self.consume()?.produce_op(Punct::XorXor), Ok('^') => self.consume()?.produce_op(Kind::XorXor),
_ => self.produce_op(Punct::Xor), _ => self.produce_op(Kind::Xor),
} }
} }
} }
/// Comments /// Comments
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn line_comment(&mut self) -> LResult<Token> { fn line_comment(&mut self) -> LResult<Token> {
let mut comment = String::new();
while Ok('\n') != self.peek() { while Ok('\n') != self.peek() {
self.consume()?; comment.push(self.next()?);
} }
self.produce(Kind::Comment, ()) self.produce(Kind::Comment, comment)
} }
fn block_comment(&mut self) -> LResult<Token> { fn block_comment(&mut self) -> LResult<Token> {
let mut comment = String::new();
while let Ok(c) = self.next() { while let Ok(c) = self.next() {
if '*' == c && Ok('/') == self.next() { if '*' == c && Ok('/') == self.peek() {
break; break;
} }
comment.push(c);
} }
self.produce(Kind::Comment, ()) self.consume()?.produce(Kind::Comment, comment)
} }
} }
/// Identifiers /// Identifiers
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn identifier(&mut self) -> LResult<Token> { fn identifier(&mut self) -> LResult<Token> {
let mut out = String::from(self.xid_start()?); let mut out = String::from(self.xid_start()?);
while let Ok(c) = self.xid_continue() { while let Ok(c) = self.xid_continue() {
@@ -362,23 +371,39 @@ impl<'t> Lexer<'t> {
} }
} }
/// Integers /// Integers
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn int_with_base(&mut self) -> LResult<Token> { fn int_with_base(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('x') => self.consume()?.digits::<16>(), Ok('x') => self.consume()?.digits::<16>(),
Ok('d') => self.consume()?.digits::<10>(), Ok('d') => self.consume()?.digits::<10>(),
Ok('o') => self.consume()?.digits::<8>(), Ok('o') => self.consume()?.digits::<8>(),
Ok('b') => self.consume()?.digits::<2>(), Ok('b') => self.consume()?.digits::<2>(),
Ok('0'..='9') => self.digits::<10>(), Ok('0'..='9' | '.') => self.digits::<10>(),
_ => self.produce(Kind::Literal, 0), _ => self.produce(Kind::Literal, 0),
} }
} }
fn digits<const B: u32>(&mut self) -> LResult<Token> { fn digits<const B: u32>(&mut self) -> LResult<Token> {
let mut value = self.digit::<B>()? as u128; let mut value = 0;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) { while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
value = value * B as u128 + self.digit::<B>()? as u128; value = value * B as u128 + self.digit::<B>()? as u128;
} }
self.produce(Kind::Literal, value) // TODO: find a better way to handle floats in the tokenizer
match self.peek() {
Ok('.') => {
// FIXME: hack: 0.. is not [0.0, '.']
if let Ok('.') = self.clone().consume()?.next() {
return self.produce(Kind::Literal, value);
}
let mut float = format!("{value}.");
self.consume()?;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_digit) {
float.push(self.iter.next().unwrap_or_default());
}
let float = f64::from_str(&float).expect("must be parsable as float");
self.produce(Kind::Literal, float)
}
_ => self.produce(Kind::Literal, value),
}
} }
fn digit<const B: u32>(&mut self) -> LResult<u32> { fn digit<const B: u32>(&mut self) -> LResult<u32> {
let digit = self.peek()?; let digit = self.peek()?;
@@ -389,7 +414,7 @@ impl<'t> Lexer<'t> {
} }
} }
/// Strings and characters /// Strings and characters
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn string(&mut self) -> LResult<Token> { fn string(&mut self) -> LResult<Token> {
let mut value = String::new(); let mut value = String::new();
while '"' while '"'

View File

@@ -110,7 +110,7 @@ mod string {
} }
mod punct { mod punct {
macro op($op:ident) { macro op($op:ident) {
TokenKind::Punct(Punct::$op) TokenKind::$op
} }
use super::*; use super::*;

View File

@@ -99,6 +99,7 @@ pub enum Parsing {
BinaryKind, BinaryKind,
Unary, Unary,
UnaryKind, UnaryKind,
Cast,
Index, Index,
Structor, Structor,
Fielder, Fielder,
@@ -118,6 +119,10 @@ pub enum Parsing {
Break, Break,
Return, Return,
Continue, Continue,
Pattern,
Match,
MatchArm,
} }
impl Display for Error { impl Display for Error {
@@ -204,6 +209,7 @@ impl Display for Parsing {
Parsing::BinaryKind => "a binary operator", Parsing::BinaryKind => "a binary operator",
Parsing::Unary => "a unary expression", Parsing::Unary => "a unary expression",
Parsing::UnaryKind => "a unary operator", Parsing::UnaryKind => "a unary operator",
Parsing::Cast => "an `as`-casting expression",
Parsing::Index => "an indexing expression", Parsing::Index => "an indexing expression",
Parsing::Structor => "a struct constructor expression", Parsing::Structor => "a struct constructor expression",
Parsing::Fielder => "a struct field expression", Parsing::Fielder => "a struct field expression",
@@ -223,6 +229,10 @@ impl Display for Parsing {
Parsing::Break => "a break expression", Parsing::Break => "a break expression",
Parsing::Return => "a return expression", Parsing::Return => "a return expression",
Parsing::Continue => "a continue expression", Parsing::Continue => "a continue expression",
Parsing::Pattern => "a pattern",
Parsing::Match => "a match expression",
Parsing::MatchArm => "a match arm",
} }
.fmt(f) .fmt(f)
} }

View File

@@ -85,7 +85,7 @@ impl Fold for ModuleInliner {
Ok(file) => file, Ok(file) => file,
}; };
let kind = match Parser::new(Lexer::new(&file)).file() { let kind = match Parser::new(Lexer::new(&file)).parse() {
Err(e) => return self.handle_parse_error(e), Err(e) => return self.handle_parse_error(e),
Ok(file) => ModuleKind::Inline(file), Ok(file) => ModuleKind::Inline(file),
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,381 @@
//! Parses an [ExprKind] using a modified pratt parser
//!
//! See also: [Expr::parse], [ExprKind::parse]
//!
//! Implementer's note: [ExprKind::parse] is the public API for parsing [ExprKind]s.
//! Do not call it from within this function.
use super::{Parse, *};
/// Parses an [ExprKind]
pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
let parsing = Parsing::ExprKind;
// Prefix expressions
let mut head = match p.peek_kind(Parsing::Unary)? {
literal_like!() => Literal::parse(p)?.into(),
path_like!() => exprkind_pathlike(p)?,
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
TokenKind::Grave => Quote::parse(p)?.into(),
TokenKind::LCurly => Block::parse(p)?.into(),
TokenKind::LBrack => exprkind_arraylike(p)?,
TokenKind::LParen => exprkind_tuplelike(p)?,
TokenKind::Let => Let::parse(p)?.into(),
TokenKind::Match => Match::parse(p)?.into(),
TokenKind::While => ExprKind::While(While::parse(p)?),
TokenKind::If => ExprKind::If(If::parse(p)?),
TokenKind::For => ExprKind::For(For::parse(p)?),
TokenKind::Break => ExprKind::Break(Break::parse(p)?),
TokenKind::Return => ExprKind::Return(Return::parse(p)?),
TokenKind::Continue => {
p.consume_peeked();
ExprKind::Continue
}
op => {
let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
let ((), after) = prec.prefix().expect("should have a precedence");
p.consume_peeked();
Unary { kind, tail: exprkind(p, after)?.into() }.into()
}
};
fn from_postfix(op: TokenKind) -> Option<Precedence> {
Some(match op {
TokenKind::LBrack => Precedence::Index,
TokenKind::LParen => Precedence::Call,
TokenKind::Dot => Precedence::Member,
_ => None?,
})
}
while let Ok(op) = p.peek_kind(parsing) {
// Postfix expressions
if let Some((before, ())) = from_postfix(op).and_then(Precedence::postfix) {
if before < power {
break;
}
p.consume_peeked();
head = match op {
TokenKind::LBrack => {
let indices =
sep(Expr::parse, TokenKind::Comma, TokenKind::RBrack, parsing)(p)?;
p.match_type(TokenKind::RBrack, parsing)?;
ExprKind::Index(Index { head: head.into(), indices })
}
TokenKind::LParen => {
let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
p.match_type(TokenKind::RParen, parsing)?;
Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() }
.into()
}
TokenKind::Dot => {
let kind = MemberKind::parse(p)?;
Member { head: Box::new(head), kind }.into()
}
_ => Err(p.error(Unexpected(op), parsing))?,
};
continue;
}
// infix expressions
if let Some((kind, prec)) = from_infix(op) {
let (before, after) = prec.infix().expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = exprkind(p, after)?;
head = Binary { kind, parts: (head, tail).into() }.into();
continue;
}
if let Some((kind, prec)) = from_modify(op) {
let (before, after) = prec.infix().expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = exprkind(p, after)?;
head = Modify { kind, parts: (head, tail).into() }.into();
continue;
}
if let TokenKind::Eq = op {
let (before, after) = Precedence::Assign
.infix()
.expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = exprkind(p, after)?;
head = Assign { parts: (head, tail).into() }.into();
continue;
}
if let TokenKind::As = op {
let before = Precedence::Cast.level();
if before < power {
break;
}
p.consume_peeked();
let ty = Ty::parse(p)?;
head = Cast { head: head.into(), ty }.into();
continue;
}
break;
}
Ok(head)
}
/// [Array] = '[' ([Expr] ',')* [Expr]? ']'
///
/// Array and ArrayRef are ambiguous until the second token,
/// so they can't be independent subexpressions
fn exprkind_arraylike(p: &mut Parser) -> PResult<ExprKind> {
const P: Parsing = Parsing::Array;
const START: TokenKind = TokenKind::LBrack;
const END: TokenKind = TokenKind::RBrack;
p.match_type(START, P)?;
let out = match p.peek_kind(P)? {
END => Array { values: vec![] }.into(),
_ => exprkind_array_rep(p)?,
};
p.match_type(END, P)?;
Ok(out)
}
/// [ArrayRep] = `[` [Expr] `;` [Expr] `]`
fn exprkind_array_rep(p: &mut Parser) -> PResult<ExprKind> {
const P: Parsing = Parsing::Array;
const END: TokenKind = TokenKind::RBrack;
let first = Expr::parse(p)?;
Ok(match p.peek_kind(P)? {
TokenKind::Semi => ArrayRep {
value: first.kind.into(),
repeat: {
p.consume_peeked();
Box::new(exprkind(p, 0)?)
},
}
.into(),
TokenKind::RBrack => Array { values: vec![first] }.into(),
TokenKind::Comma => Array {
values: {
p.consume_peeked();
let mut out = vec![first];
out.extend(sep(Expr::parse, TokenKind::Comma, END, P)(p)?);
out
},
}
.into(),
ty => Err(p.error(Unexpected(ty), P))?,
})
}
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
///
/// [ExprKind::Empty] and [Group] are special cases of [Tuple]
fn exprkind_tuplelike(p: &mut Parser) -> PResult<ExprKind> {
p.match_type(TokenKind::LParen, Parsing::Group)?;
let out = match p.peek_kind(Parsing::Group)? {
TokenKind::RParen => Ok(ExprKind::Empty),
_ => exprkind_group(p),
};
p.match_type(TokenKind::RParen, Parsing::Group)?;
out
}
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
fn exprkind_group(p: &mut Parser) -> PResult<ExprKind> {
let first = Expr::parse(p)?;
match p.peek_kind(Parsing::Group)? {
TokenKind::Comma => {
let mut exprs = vec![first];
p.consume_peeked();
while TokenKind::RParen != p.peek_kind(Parsing::Tuple)? {
exprs.push(Expr::parse(p)?);
match p.peek_kind(Parsing::Tuple)? {
TokenKind::Comma => p.consume_peeked(),
_ => break,
};
}
Ok(Tuple { exprs }.into())
}
_ => Ok(Group { expr: first.kind.into() }.into()),
}
}
/// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor])
fn exprkind_pathlike(p: &mut Parser) -> PResult<ExprKind> {
let head = Path::parse(p)?;
Ok(match p.match_type(TokenKind::Colon, Parsing::Path) {
Ok(_) => ExprKind::Structor(structor_body(p, head)?),
Err(_) => ExprKind::Path(head),
})
}
/// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}`
fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
let init = delim(
sep(
Fielder::parse,
TokenKind::Comma,
CURLIES.1,
Parsing::Structor,
),
CURLIES,
Parsing::Structor,
)(p)?;
Ok(Structor { to, init })
}
/// Precedence provides a total ordering among operators
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Precedence {
Assign,
Logic,
Compare,
Range,
Bitwise,
Shift,
Factor,
Term,
Unary,
Index,
Cast,
Member, // left-associative
Call,
Highest,
}
impl Precedence {
#[inline]
pub const fn level(self) -> u8 {
(self as u8) << 1
}
pub fn prefix(self) -> Option<((), u8)> {
match self {
Self::Assign => Some(((), self.level())),
Self::Unary => Some(((), self.level())),
_ => None,
}
}
pub fn infix(self) -> Option<(u8, u8)> {
let level = self.level();
match self {
Self::Unary => None,
Self::Assign => Some((level + 1, level)),
_ => Some((level, level + 1)),
}
}
pub fn postfix(self) -> Option<(u8, ())> {
match self {
Self::Index | Self::Call | Self::Member => Some((self.level(), ())),
_ => None,
}
}
}
impl From<ModifyKind> for Precedence {
fn from(_value: ModifyKind) -> Self {
Precedence::Assign
}
}
impl From<BinaryKind> for Precedence {
fn from(value: BinaryKind) -> Self {
use BinaryKind as Op;
match value {
Op::Call => Precedence::Call,
Op::Mul | Op::Div | Op::Rem => Precedence::Term,
Op::Add | Op::Sub => Precedence::Factor,
Op::Shl | Op::Shr => Precedence::Shift,
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
Op::RangeExc | Op::RangeInc => Precedence::Range,
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
}
}
}
impl From<UnaryKind> for Precedence {
fn from(value: UnaryKind) -> Self {
use UnaryKind as Op;
match value {
Op::Loop => Precedence::Assign,
Op::Deref | Op::Neg | Op::Not | Op::At | Op::Tilde => Precedence::Unary,
}
}
}
/// Creates helper functions for turning TokenKinds into AST operators
macro operator($($name:ident ($takes:ident => $returns:ident) {$($t:ident => $p:ident),*$(,)?};)*) {$(
pub fn $name (value: $takes) -> Option<($returns, Precedence)> {
match value {
$($takes::$t => Some(($returns::$p, Precedence::from($returns::$p))),)*
_ => None?,
}
})*
}
operator! {
from_prefix (TokenKind => UnaryKind) {
Loop => Loop,
Star => Deref,
Minus => Neg,
Bang => Not,
At => At,
Tilde => Tilde,
};
from_modify(TokenKind => ModifyKind) {
AmpEq => And,
BarEq => Or,
XorEq => Xor,
LtLtEq => Shl,
GtGtEq => Shr,
PlusEq => Add,
MinusEq => Sub,
StarEq => Mul,
SlashEq => Div,
RemEq => Rem,
};
from_infix (TokenKind => BinaryKind) {
Lt => Lt,
LtEq => LtEq,
EqEq => Equal,
BangEq => NotEq,
GtEq => GtEq,
Gt => Gt,
DotDot => RangeExc,
DotDotEq => RangeInc,
AmpAmp => LogAnd,
BarBar => LogOr,
XorXor => LogXor,
Amp => BitAnd,
Bar => BitOr,
Xor => BitXor,
LtLt => Shl,
GtGt => Shr,
Plus => Add,
Minus => Sub,
Star => Mul,
Slash => Div,
Rem => Rem,
};
}

View File

@@ -1,5 +1,6 @@
//! Pretty prints a conlang AST in yaml //! Pretty prints a conlang AST in yaml
use cl_ast::Stmt;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use repline::{error::Error as RlError, Repline}; use repline::{error::Error as RlError, Repline};
@@ -19,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}; };
let mut parser = Parser::new(Lexer::new(&line)); let mut parser = Parser::new(Lexer::new(&line));
let code = match parser.stmt() { let code = match parser.parse::<Stmt>() {
Ok(code) => { Ok(code) => {
rl.accept(); rl.accept();
code code
@@ -119,19 +120,19 @@ pub mod yamler {
} }
} }
impl<'y> Deref for Section<'y> { impl Deref for Section<'_> {
type Target = Yamler; type Target = Yamler;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.yamler self.yamler
} }
} }
impl<'y> DerefMut for Section<'y> { impl DerefMut for Section<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.yamler self.yamler
} }
} }
impl<'y> Drop for Section<'y> { impl Drop for Section<'_> {
fn drop(&mut self) { fn drop(&mut self) {
let Self { yamler } = self; let Self { yamler } = self;
yamler.decrease(); yamler.decrease();
@@ -363,22 +364,11 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
StmtKind::Empty => y, StmtKind::Empty => y,
StmtKind::Local(s) => y.yaml(s),
StmtKind::Item(s) => y.yaml(s), StmtKind::Item(s) => y.yaml(s),
StmtKind::Expr(s) => y.yaml(s), StmtKind::Expr(s) => y.yaml(s),
}; };
} }
} }
impl Yamlify for Let {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key("Let")
.pair("name", name)
.yaml(mutable)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Expr { impl Yamlify for Expr {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self; let Self { extents: _, kind } = self;
@@ -388,10 +378,14 @@ pub mod yamlify {
impl Yamlify for ExprKind { impl Yamlify for ExprKind {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
ExprKind::Quote(k) => k.yaml(y),
ExprKind::Let(k) => k.yaml(y),
ExprKind::Match(k) => k.yaml(y),
ExprKind::Assign(k) => k.yaml(y), ExprKind::Assign(k) => k.yaml(y),
ExprKind::Modify(k) => k.yaml(y), ExprKind::Modify(k) => k.yaml(y),
ExprKind::Binary(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y),
ExprKind::Cast(k) => k.yaml(y),
ExprKind::Member(k) => k.yaml(y), ExprKind::Member(k) => k.yaml(y),
ExprKind::Index(k) => k.yaml(y), ExprKind::Index(k) => k.yaml(y),
ExprKind::Structor(k) => k.yaml(y), ExprKind::Structor(k) => k.yaml(y),
@@ -404,16 +398,71 @@ pub mod yamlify {
ExprKind::Empty => {} ExprKind::Empty => {}
ExprKind::Group(k) => k.yaml(y), ExprKind::Group(k) => k.yaml(y),
ExprKind::Tuple(k) => k.yaml(y), ExprKind::Tuple(k) => k.yaml(y),
ExprKind::Loop(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y), ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y), ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y), ExprKind::For(k) => k.yaml(y),
ExprKind::Break(k) => k.yaml(y), ExprKind::Break(k) => k.yaml(y),
ExprKind::Return(k) => k.yaml(y), ExprKind::Return(k) => k.yaml(y),
ExprKind::Continue(k) => k.yaml(y), ExprKind::Continue => {
y.key("Continue");
}
} }
} }
} }
impl Yamlify for Quote {
fn yaml(&self, y: &mut Yamler) {
y.key("Quote").value(self);
}
}
impl Yamlify for Let {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key("Let")
.pair("name", name)
.yaml(mutable)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Pattern {
fn yaml(&self, y: &mut Yamler) {
match self {
Pattern::Path(path) => y.value(path),
Pattern::Literal(literal) => y.value(literal),
Pattern::Ref(mutability, pattern) => {
y.pair("mutability", mutability).pair("subpattern", pattern)
}
Pattern::Tuple(patterns) => y.key("Tuple").yaml(patterns),
Pattern::Array(patterns) => y.key("Array").yaml(patterns),
Pattern::Struct(path, items) => {
{
let mut y = y.key("Struct");
y.pair("name", path);
for (name, item) in items {
y.pair(name, item);
}
}
y
}
};
}
}
impl Yamlify for Match {
fn yaml(&self, y: &mut Yamler) {
let Self { scrutinee, arms } = self;
y.key("Match")
.pair("scrutinee", scrutinee)
.pair("arms", arms);
}
}
impl Yamlify for MatchArm {
fn yaml(&self, y: &mut Yamler) {
let Self(pat, expr) = self;
y.pair("pat", pat).pair("expr", expr);
}
}
impl Yamlify for Assign { impl Yamlify for Assign {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { parts } = self; let Self { parts } = self;
@@ -461,6 +510,12 @@ pub mod yamlify {
y.value(self); y.value(self);
} }
} }
impl Yamlify for Cast {
fn yaml(&self, y: &mut Yamler) {
let Self { head, ty } = self;
y.key("Cast").pair("head", head).pair("ty", ty);
}
}
impl Yamlify for Member { impl Yamlify for Member {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { head, kind } = self; let Self { head, kind } = self;
@@ -516,11 +571,8 @@ pub mod yamlify {
} }
impl Yamlify for AddrOf { impl Yamlify for AddrOf {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { count, mutable, expr } = self; let Self { mutable, expr } = self;
y.key("AddrOf") y.key("AddrOf").yaml(mutable).pair("expr", expr);
.pair("count", count)
.yaml(mutable)
.pair("expr", expr);
} }
} }
impl Yamlify for Group { impl Yamlify for Group {
@@ -529,12 +581,6 @@ pub mod yamlify {
y.key("Group").yaml(expr); y.key("Group").yaml(expr);
} }
} }
impl Yamlify for Loop {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Loop").yaml(body);
}
}
impl Yamlify for While { impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
@@ -578,11 +624,6 @@ pub mod yamlify {
y.key("Return").yaml(body); y.key("Return").yaml(body);
} }
} }
impl Yamlify for Continue {
fn yaml(&self, y: &mut Yamler) {
y.key("Continue");
}
}
impl Yamlify for Literal { impl Yamlify for Literal {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
y.value(format_args!("\"{self}\"")); y.value(format_args!("\"{self}\""));

View File

@@ -35,6 +35,10 @@ argwerk::define! {
[#[option] path] if file.is_none() => { [#[option] path] if file.is_none() => {
file = path.map(Into::into); file = path.map(Into::into);
} }
[path] if file.is_some() => {
include.push(path.into());
}
} }
/// gets whether stdin AND stdout are a terminal, for pipelining /// gets whether stdin AND stdout are a terminal, for pipelining

View File

@@ -5,7 +5,8 @@ use crate::{
menu, menu,
tools::print_token, tools::print_token,
}; };
use cl_interpret::{convalue::ConValue, env::Environment, interpret::Interpret}; use cl_ast::File;
use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use std::{error::Error, path::Path}; use std::{error::Error, path::Path};
@@ -15,6 +16,31 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
let Args { file, include, mode, repl } = args; let Args { file, include, mode, repl } = args;
let mut env = Environment::new(); let mut env = Environment::new();
env.add_builtins(&builtins! {
/// Clears the screen
fn clear() {
menu::clear();
Ok(ConValue::Empty)
}
/// Evaluates a quoted expression
fn eval(ConValue::Quote(quote)) @env {
env.eval(quote.as_ref())
}
/// Executes a file
fn import(ConValue::String(path)) @env {
load_file(env, &**path).or(Ok(ConValue::Empty))
}
/// Gets a line of input from stdin
fn get_line() {
match repline::Repline::new("", "", "").read() {
Ok(line) => Ok(ConValue::String(line.into())),
Err(e) => Ok(ConValue::String(e.to_string().into())),
}
}
});
for path in include { for path in include {
load_file(&mut env, path)?; load_file(&mut env, path)?;
} }
@@ -49,7 +75,7 @@ fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue,
let inliner = let inliner =
cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new(""))); cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new("")));
let file = std::fs::read_to_string(path)?; let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).file()?; let code = Parser::new(Lexer::new(&file)).parse()?;
let code = match inliner.inline(code) { let code = match inliner.inline(code) {
Ok(a) => a, Ok(a) => a,
Err((code, io_errs, parse_errs)) => { Err((code, io_errs, parse_errs)) => {
@@ -79,13 +105,13 @@ fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Er
} }
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> { fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?; let code = Parser::new(Lexer::new(code)).parse::<File>()?;
println!("{code}"); println!("{code}");
Ok(()) Ok(())
} }
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> { fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?; let code = Parser::new(Lexer::new(code)).parse::<File>()?;
match code.interpret(env)? { match code.interpret(env)? {
ConValue::Empty => {} ConValue::Empty => {}
ret => println!("{ret}"), ret => println!("{ret}"),

View File

@@ -1,6 +1,4 @@
use cl_interpret::{ use cl_interpret::{convalue::ConValue, env::Environment, error::IResult, interpret::Interpret};
env::Environment, error::IResult, interpret::Interpret, convalue::ConValue,
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Context { pub struct Context {

View File

@@ -1,10 +1,12 @@
use crate::{ansi, ctx}; use crate::{ansi, ctx};
use cl_ast::Stmt;
use cl_interpret::convalue::ConValue;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use repline::{error::ReplResult, prebaked::*}; use repline::{error::ReplResult, prebaked::*};
fn clear() { pub fn clear() {
println!("{}", ansi::CLEAR_ALL); print!("{}", ansi::CLEAR_ALL);
banner() banner()
} }
@@ -42,11 +44,15 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
use cl_parser::inliner::ModuleInliner; use cl_parser::inliner::ModuleInliner;
read_and(ansi::CYAN, "cl>", " ?>", |line| { read_and(ansi::CYAN, "cl>", " ?>", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?; if line.trim().is_empty() {
return Ok(Response::Deny);
}
let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?;
let code = ModuleInliner::new(".").fold_stmt(code); let code = ModuleInliner::new(".").fold_stmt(code);
print!("{}", ansi::OUTPUT); print!("{}", ansi::OUTPUT);
match ctx.run(&code) { match ctx.run(&code) {
Ok(ConValue::Empty) => print!("{}", ansi::RESET),
Ok(v) => println!("{}{v}", ansi::RESET), Ok(v) => println!("{}{v}", ansi::RESET),
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET), Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
} }
@@ -71,7 +77,7 @@ pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| { read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
let mut p = Parser::new(Lexer::new(line)); let mut p = Parser::new(Lexer::new(line));
match p.stmt() { match p.parse::<Stmt>() {
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET), Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
Err(e) => Err(e)?, Err(e) => Err(e)?,
} }

View File

@@ -8,4 +8,4 @@ license.workspace = true
publish.workspace = true publish.workspace = true
[dependencies] [dependencies]
cl-arena = { path = "../cl-arena" } cl-arena = { version = "0", registry = "soft-fish" }

View File

@@ -106,7 +106,7 @@ impl<V, K: MapIndex> IndexMap<K, V> {
pub fn get_many_mut<const N: usize>( pub fn get_many_mut<const N: usize>(
&mut self, &mut self,
indices: [K; N], indices: [K; N],
) -> Result<[&mut V; N], GetManyMutError<N>> { ) -> Result<[&mut V; N], GetManyMutError> {
self.map.get_many_mut(indices.map(|id| id.get())) self.map.get_many_mut(indices.map(|id| id.get()))
} }

View File

@@ -35,9 +35,14 @@ pub mod interned {
pub fn as_ptr(interned: &Self) -> *const T { pub fn as_ptr(interned: &Self) -> *const T {
interned.value interned.value
} }
/// Gets the internal value as a reference with the interner's lifetime
pub fn to_ref(interned: &Self) -> &'a T {
interned.value
}
} }
impl<'a, T: ?Sized + Debug> Debug for Interned<'a, T> { impl<T: ?Sized + Debug> Debug for Interned<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Interned") f.debug_struct("Interned")
.field("value", &self.value) .field("value", &self.value)
@@ -49,14 +54,14 @@ pub mod interned {
Self { value } Self { value }
} }
} }
impl<'a, T: ?Sized> Deref for Interned<'a, T> { impl<T: ?Sized> Deref for Interned<'_, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.value self.value
} }
} }
impl<'a, T: ?Sized> Copy for Interned<'a, T> {} impl<T: ?Sized> Copy for Interned<'_, T> {}
impl<'a, T: ?Sized> Clone for Interned<'a, T> { impl<T: ?Sized> Clone for Interned<'_, T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self
} }
@@ -79,13 +84,13 @@ pub mod interned {
// } // }
// } // }
impl<'a, T: ?Sized> Eq for Interned<'a, T> {} impl<T: ?Sized> Eq for Interned<'_, T> {}
impl<'a, T: ?Sized> PartialEq for Interned<'a, T> { impl<T: ?Sized> PartialEq for Interned<'_, T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.value, other.value) std::ptr::eq(self.value, other.value)
} }
} }
impl<'a, T: ?Sized> Hash for Interned<'a, T> { impl<T: ?Sized> Hash for Interned<'_, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Self::as_ptr(self).hash(state) Self::as_ptr(self).hash(state)
} }
@@ -119,6 +124,7 @@ pub mod string_interner {
}; };
/// A string interner hands out [Interned] copies of each unique string given to it. /// A string interner hands out [Interned] copies of each unique string given to it.
#[derive(Default)]
pub struct StringInterner<'a> { pub struct StringInterner<'a> {
arena: DroplessArena<'a>, arena: DroplessArena<'a>,
keys: RwLock<HashSet<&'a str>>, keys: RwLock<HashSet<&'a str>>,
@@ -185,12 +191,30 @@ pub mod string_interner {
} }
} }
impl std::fmt::Display for StringInterner<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Ok(keys) = self.keys.read() else {
return write!(f, "Could not lock StringInterner key map.");
};
let mut keys: Vec<_> = keys.iter().collect();
keys.sort();
writeln!(f, "Keys:")?;
for (idx, key) in keys.iter().enumerate() {
writeln!(f, "{idx}:\t\"{key}\"")?
}
writeln!(f, "Count: {}", keys.len())?;
Ok(())
}
}
// # Safety: // # Safety:
// This is fine because StringInterner::get_or_insert(v) holds a RwLock // This is fine because StringInterner::get_or_insert(v) holds a RwLock
// for its entire duration, and doesn't touch the non-(Send+Sync) arena // for its entire duration, and doesn't touch the non-(Send+Sync) arena
// unless the lock is held by a write guard. // unless the lock is held by a write guard.
unsafe impl<'a> Send for StringInterner<'a> {} unsafe impl Send for StringInterner<'_> {}
unsafe impl<'a> Sync for StringInterner<'a> {} unsafe impl Sync for StringInterner<'_> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@@ -240,6 +264,7 @@ pub mod typed_interner {
/// A [TypedInterner] hands out [Interned] references for arbitrary types. /// A [TypedInterner] hands out [Interned] references for arbitrary types.
/// ///
/// See the [module-level documentation](self) for more information. /// See the [module-level documentation](self) for more information.
#[derive(Default)]
pub struct TypedInterner<'a, T: Eq + Hash> { pub struct TypedInterner<'a, T: Eq + Hash> {
arena: TypedArena<'a, T>, arena: TypedArena<'a, T>,
keys: RwLock<HashSet<&'a T>>, keys: RwLock<HashSet<&'a T>>,
@@ -286,5 +311,5 @@ pub mod typed_interner {
/// [get_or_insert](TypedInterner::get_or_insert) are unique, and the function uses /// [get_or_insert](TypedInterner::get_or_insert) are unique, and the function uses
/// the [RwLock] around the [HashSet] to ensure mutual exclusion /// the [RwLock] around the [HashSet] to ensure mutual exclusion
unsafe impl<'a, T: Eq + Hash + Send> Send for TypedInterner<'a, T> where &'a T: Send {} unsafe impl<'a, T: Eq + Hash + Send> Send for TypedInterner<'a, T> where &'a T: Send {}
unsafe impl<'a, T: Eq + Hash + Send + Sync> Sync for TypedInterner<'a, T> {} unsafe impl<T: Eq + Hash + Send + Sync> Sync for TypedInterner<'_, T> {}
} }

View File

@@ -10,4 +10,4 @@ pub mod token_type;
pub use token::Token; pub use token::Token;
pub use token_data::TokenData; pub use token_data::TokenData;
pub use token_type::{Punct, TokenKind}; pub use token_type::TokenKind;

View File

@@ -13,41 +13,36 @@ pub enum TokenKind {
/// A non-keyword identifier /// A non-keyword identifier
Identifier, Identifier,
// A keyword // A keyword
As, As, // as
Break, Break, // "break"
Cl, Cl, // "cl"
Const, Const, // "const"
Continue, Continue, // "continue"
Else, Else, // "else"
Enum, Enum, // "enum"
False, False, // "false"
For, Fn, // "fn"
Fn, For, // "for"
If, If, // "if"
Impl, Impl, // "impl"
In, In, // "in"
Let, Let, // "let"
Loop, Loop, // "loop"
Mod, Match, // "match"
Mut, Mod, // "mod"
Pub, Mut, // "mut"
Return, Pub, // "pub"
SelfKw, Return, // "return"
SelfTy, SelfKw, // "self"
Static, SelfTy, // "Self"
Struct, Static, // "static"
Super, Struct, // "struct"
True, Super, // "super"
Type, True, // "true"
Use, Type, // "type"
While, Use, // "use"
/// Delimiter or punctuation While, // "while"
Punct(Punct), // Delimiter or punctuation
}
/// An operator character (delimiter, punctuation)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Punct {
LCurly, // { LCurly, // {
RCurly, // } RCurly, // }
LBrack, // [ LBrack, // [
@@ -112,36 +107,90 @@ impl Display for TokenKind {
TokenKind::Literal => "literal".fmt(f), TokenKind::Literal => "literal".fmt(f),
TokenKind::Identifier => "identifier".fmt(f), TokenKind::Identifier => "identifier".fmt(f),
TokenKind::As => "as".fmt(f), TokenKind::As => "sama".fmt(f),
TokenKind::Break => "break".fmt(f), TokenKind::Break => "pana".fmt(f),
TokenKind::Cl => "cl".fmt(f), TokenKind::Cl => "la".fmt(f),
TokenKind::Const => "const".fmt(f), TokenKind::Const => "kiwen".fmt(f),
TokenKind::Continue => "continue".fmt(f), TokenKind::Continue => "tawa".fmt(f),
TokenKind::Else => "else".fmt(f), TokenKind::Else => "taso".fmt(f),
TokenKind::Enum => "enum".fmt(f), TokenKind::Enum => "kulupu".fmt(f),
TokenKind::False => "false".fmt(f), TokenKind::False => "ike".fmt(f),
TokenKind::For => "for".fmt(f), TokenKind::Fn => "nasin".fmt(f),
TokenKind::Fn => "fn".fmt(f), TokenKind::For => "ale".fmt(f),
TokenKind::If => "if".fmt(f), TokenKind::If => "tan".fmt(f),
TokenKind::Impl => "impl".fmt(f), TokenKind::Impl => "insa".fmt(f),
TokenKind::In => "in".fmt(f), TokenKind::In => "lon".fmt(f),
TokenKind::Let => "let".fmt(f), TokenKind::Let => "poki".fmt(f),
TokenKind::Loop => "loop".fmt(f), TokenKind::Loop => "awen".fmt(f),
TokenKind::Mod => "mod".fmt(f), TokenKind::Match => "seme".fmt(f),
TokenKind::Mut => "mut".fmt(f), TokenKind::Mod => "selo".fmt(f),
TokenKind::Pub => "pub".fmt(f), TokenKind::Mut => "ante".fmt(f),
TokenKind::Return => "return".fmt(f), TokenKind::Pub => "lukin".fmt(f),
TokenKind::SelfKw => "self".fmt(f), TokenKind::Return => "pini".fmt(f),
TokenKind::SelfTy => "Self".fmt(f), TokenKind::SelfKw => "mi".fmt(f),
TokenKind::Static => "static".fmt(f), TokenKind::SelfTy => "Mi".fmt(f),
TokenKind::Struct => "struct".fmt(f), TokenKind::Static => "mute".fmt(f),
TokenKind::Super => "super".fmt(f), TokenKind::Struct => "lipu".fmt(f),
TokenKind::True => "true".fmt(f), TokenKind::Super => "mama".fmt(f),
TokenKind::Type => "type".fmt(f), TokenKind::True => "pona".fmt(f),
TokenKind::Use => "use".fmt(f), TokenKind::Type => "ijo".fmt(f),
TokenKind::While => "while".fmt(f), TokenKind::Use => "jo".fmt(f),
TokenKind::While => "lawa".fmt(f),
TokenKind::Punct(op) => op.fmt(f), TokenKind::LCurly => "{".fmt(f),
TokenKind::RCurly => "}".fmt(f),
TokenKind::LBrack => "[".fmt(f),
TokenKind::RBrack => "]".fmt(f),
TokenKind::LParen => "(".fmt(f),
TokenKind::RParen => ")".fmt(f),
TokenKind::Amp => "&".fmt(f),
TokenKind::AmpAmp => "&&".fmt(f),
TokenKind::AmpEq => "&=".fmt(f),
TokenKind::Arrow => "->".fmt(f),
TokenKind::At => "@".fmt(f),
TokenKind::Backslash => "\\".fmt(f),
TokenKind::Bang => "!".fmt(f),
TokenKind::BangBang => "!!".fmt(f),
TokenKind::BangEq => "!=".fmt(f),
TokenKind::Bar => "|".fmt(f),
TokenKind::BarBar => "||".fmt(f),
TokenKind::BarEq => "|=".fmt(f),
TokenKind::Colon => ":".fmt(f),
TokenKind::ColonColon => "::".fmt(f),
TokenKind::Comma => ",".fmt(f),
TokenKind::Dot => ".".fmt(f),
TokenKind::DotDot => "..".fmt(f),
TokenKind::DotDotEq => "..=".fmt(f),
TokenKind::Eq => "=".fmt(f),
TokenKind::EqEq => "==".fmt(f),
TokenKind::FatArrow => "=>".fmt(f),
TokenKind::Grave => "`".fmt(f),
TokenKind::Gt => ">".fmt(f),
TokenKind::GtEq => ">=".fmt(f),
TokenKind::GtGt => ">>".fmt(f),
TokenKind::GtGtEq => ">>=".fmt(f),
TokenKind::Hash => "#".fmt(f),
TokenKind::HashBang => "#!".fmt(f),
TokenKind::Lt => "<".fmt(f),
TokenKind::LtEq => "<=".fmt(f),
TokenKind::LtLt => "<<".fmt(f),
TokenKind::LtLtEq => "<<=".fmt(f),
TokenKind::Minus => "-".fmt(f),
TokenKind::MinusEq => "-=".fmt(f),
TokenKind::Plus => "+".fmt(f),
TokenKind::PlusEq => "+=".fmt(f),
TokenKind::Question => "?".fmt(f),
TokenKind::Rem => "%".fmt(f),
TokenKind::RemEq => "%=".fmt(f),
TokenKind::Semi => ";".fmt(f),
TokenKind::Slash => "/".fmt(f),
TokenKind::SlashEq => "/=".fmt(f),
TokenKind::Star => "*".fmt(f),
TokenKind::StarEq => "*=".fmt(f),
TokenKind::Tilde => "~".fmt(f),
TokenKind::Xor => "^".fmt(f),
TokenKind::XorEq => "^=".fmt(f),
TokenKind::XorXor => "^^".fmt(f),
} }
} }
} }
@@ -151,96 +200,36 @@ impl FromStr for TokenKind {
/// Parses a string s to return a Keyword /// Parses a string s to return a Keyword
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s { Ok(match s {
"as" => Self::As, "as" | "sama" => Self::As,
"break" => Self::Break, "break" | "pana" => Self::Break,
"cl" => Self::Cl, "cl" | "la" => Self::Cl,
"const" => Self::Const, "const" | "kiwen" => Self::Const,
"continue" => Self::Continue, "continue" | "tawa" => Self::Continue,
"else" => Self::Else, "else" | "taso" => Self::Else,
"enum" => Self::Enum, "enum" | "kulupu" => Self::Enum,
"false" => Self::False, "false" | "ike" => Self::False,
"for" => Self::For, "fn" | "nasin" => Self::Fn,
"fn" => Self::Fn, "for" | "ale" => Self::For,
"if" => Self::If, "if" | "tan" => Self::If,
"impl" => Self::Impl, "impl" | "insa" => Self::Impl,
"in" => Self::In, "in" | "lon" => Self::In,
"let" => Self::Let, "let" | "poki" => Self::Let,
"loop" => Self::Loop, "loop" | "awen" => Self::Loop,
"mod" => Self::Mod, "match" | "seme" => Self::Match,
"mut" => Self::Mut, "mod" | "selo" => Self::Mod,
"pub" => Self::Pub, "mut" | "ante" => Self::Mut,
"return" => Self::Return, "pub" | "lukin" => Self::Pub,
"self" => Self::SelfKw, "return" | "pini" => Self::Return,
"Self" => Self::SelfTy, "self" | "mi" => Self::SelfKw,
"static" => Self::Static, "Self" | "Mi" => Self::SelfTy,
"struct" => Self::Struct, "static" | "mute" => Self::Static,
"super" => Self::Super, "struct" | "lipu" => Self::Struct,
"true" => Self::True, "super" | "mama" => Self::Super,
"type" => Self::Type, "true" | "pona" => Self::True,
"use" => Self::Use, "type" | "ijo" => Self::Type,
"while" => Self::While, "use" | "jo" => Self::Use,
"while" | "lawa" => Self::While,
_ => Err(())?, _ => Err(())?,
}) })
} }
} }
impl Display for Punct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Punct::LCurly => "{".fmt(f),
Punct::RCurly => "}".fmt(f),
Punct::LBrack => "[".fmt(f),
Punct::RBrack => "]".fmt(f),
Punct::LParen => "(".fmt(f),
Punct::RParen => ")".fmt(f),
Punct::Amp => "&".fmt(f),
Punct::AmpAmp => "&&".fmt(f),
Punct::AmpEq => "&=".fmt(f),
Punct::Arrow => "->".fmt(f),
Punct::At => "@".fmt(f),
Punct::Backslash => "\\".fmt(f),
Punct::Bang => "!".fmt(f),
Punct::BangBang => "!!".fmt(f),
Punct::BangEq => "!=".fmt(f),
Punct::Bar => "|".fmt(f),
Punct::BarBar => "||".fmt(f),
Punct::BarEq => "|=".fmt(f),
Punct::Colon => ":".fmt(f),
Punct::ColonColon => "::".fmt(f),
Punct::Comma => ",".fmt(f),
Punct::Dot => ".".fmt(f),
Punct::DotDot => "..".fmt(f),
Punct::DotDotEq => "..=".fmt(f),
Punct::Eq => "=".fmt(f),
Punct::EqEq => "==".fmt(f),
Punct::FatArrow => "=>".fmt(f),
Punct::Grave => "`".fmt(f),
Punct::Gt => ">".fmt(f),
Punct::GtEq => ">=".fmt(f),
Punct::GtGt => ">>".fmt(f),
Punct::GtGtEq => ">>=".fmt(f),
Punct::Hash => "#".fmt(f),
Punct::HashBang => "#!".fmt(f),
Punct::Lt => "<".fmt(f),
Punct::LtEq => "<=".fmt(f),
Punct::LtLt => "<<".fmt(f),
Punct::LtLtEq => "<<=".fmt(f),
Punct::Minus => "-".fmt(f),
Punct::MinusEq => "-=".fmt(f),
Punct::Plus => "+".fmt(f),
Punct::PlusEq => "+=".fmt(f),
Punct::Question => "?".fmt(f),
Punct::Rem => "%".fmt(f),
Punct::RemEq => "%=".fmt(f),
Punct::Semi => ";".fmt(f),
Punct::Slash => "/".fmt(f),
Punct::SlashEq => "/=".fmt(f),
Punct::Star => "*".fmt(f),
Punct::StarEq => "*=".fmt(f),
Punct::Tilde => "~".fmt(f),
Punct::Xor => "^".fmt(f),
Punct::XorEq => "^=".fmt(f),
Punct::XorXor => "^^".fmt(f),
}
}
}

View File

@@ -1,76 +1,80 @@
use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression};
use cl_ast::{ use cl_ast::{
ast_visitor::{Fold, Visit}, ast_visitor::{Fold, Visit},
desugar::*, desugar::*,
Stmt, Ty,
}; };
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser}; use cl_parser::{inliner::ModuleInliner, Parser};
use cl_typeck::{ use cl_structures::intern::string_interner::StringInterner;
definition::Def,
handle::Handle,
name_collector::NameCollector,
node::{Node, NodeSource},
project::Project,
type_resolver::resolve,
};
use repline::{error::Error as RlError, prebaked::*}; use repline::{error::Error as RlError, prebaked::*};
use std::{error::Error, path}; use std::{
error::Error,
path::{self, PathBuf},
sync::LazyLock,
};
// Path to display in standard library errors // Path to display in standard library errors
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl"; const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
// Statically included standard library // Statically included standard library
const STDLIB: &str = include_str!("../../../stdlib/lib.cl"); const PREAMBLE: &str = r"
pub mod std;
pub use std::preamble::*;
";
// Colors // Colors
const C_MAIN: &str = ""; const C_MAIN: &str = C_LISTING;
const C_RESV: &str = "\x1b[35m"; const C_RESV: &str = "\x1b[35m";
const C_CODE: &str = "\x1b[36m"; const C_CODE: &str = "\x1b[36m";
const C_BYID: &str = "\x1b[95m"; const C_BYID: &str = "\x1b[95m";
const C_ERROR: &str = "\x1b[31m";
const C_LISTING: &str = "\x1b[38;5;117m"; const C_LISTING: &str = "\x1b[38;5;117m";
/// A home for immutable intermediate ASTs
///
/// TODO: remove this.
static mut TREES: TreeManager = TreeManager::new();
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Project::default(); let mut prj = Table::default();
let mut parser = Parser::new(Lexer::new(STDLIB)); let mut parser = Parser::new(Lexer::new(PREAMBLE));
let code = match parser.file() { let code = match parser.parse() {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
eprintln!("{STDLIB_DISPLAY_PATH}:{e}"); eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
Err(e)? Err(e)?
} }
}; };
let code = inline_modules(code, concat!(env!("PWD"), "/stdlib")); // This code is special - it gets loaded from a hard-coded project directory (for now)
NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) }); let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
Populator::new(&mut prj).visit_file(interned(code));
main_menu(&mut prj)?; main_menu(&mut prj)?;
Ok(()) Ok(())
} }
fn main_menu(prj: &mut Project) -> Result<(), RlError> { fn main_menu(prj: &mut Table) -> Result<(), RlError> {
banner(); banner();
read_and(C_MAIN, "mu>", "? >", |line| { read_and(C_MAIN, "mu>", "? >", |line| {
match line.trim() { match line.trim() {
"c" | "code" => enter_code(prj)?, "c" | "code" => enter_code(prj)?,
"clear" => clear()?, "clear" => clear()?,
"d" | "desugar" => live_desugar()?,
"e" | "exit" => return Ok(Response::Break), "e" | "exit" => return Ok(Response::Break),
"f" | "file" => import_files(prj)?,
"i" | "id" => get_by_id(prj)?,
"l" | "list" => list_types(prj), "l" | "list" => list_types(prj),
"q" | "query" => query_type_expression(prj)?, "q" | "query" => query_type_expression(prj)?,
"i" | "id" => get_by_id(prj)?,
"r" | "resolve" => resolve_all(prj)?, "r" | "resolve" => resolve_all(prj)?,
"d" | "desugar" => live_desugar()?, "s" | "strings" => print_strings(),
"h" | "help" => { "h" | "help" | "" => {
println!( println!(
"Valid commands are: "Valid commands are:
clear : Clear the screen
code (c): Enter code to type-check code (c): Enter code to type-check
desugar (d): WIP: Test the experimental desugaring passes
file (f): Load files from disk
id (i): Get a type by its type ID
list (l): List all known types list (l): List all known types
query (q): Query the type system query (q): Query the type system
id (i): Get a type by its type ID
resolve (r): Perform type resolution resolve (r): Perform type resolution
desugar (d): WIP: Test the experimental desugaring passes
help (h): Print this list help (h): Print this list
exit (e): Exit the program" exit (e): Exit the program"
); );
@@ -82,24 +86,23 @@ fn main_menu(prj: &mut Project) -> Result<(), RlError> {
}) })
} }
fn enter_code(prj: &mut Project) -> Result<(), RlError> { fn enter_code(prj: &mut Table) -> Result<(), RlError> {
read_and(C_CODE, "cl>", "? >", |line| { read_and(C_CODE, "cl>", "? >", |line| {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
let code = Parser::new(Lexer::new(line)).file()?; let code = Parser::new(Lexer::new(line)).parse()?;
let code = inline_modules(code, ""); let code = inline_modules(code, "");
let code = WhileElseDesugar.fold_file(code); let code = WhileElseDesugar.fold_file(code);
// Safety: this is totally unsafe
NameCollector::new(prj).visit_file(unsafe { TREES.push(code) });
Populator::new(prj).visit_file(interned(code));
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
fn live_desugar() -> Result<(), RlError> { fn live_desugar() -> Result<(), RlError> {
read_and(C_RESV, "se>", "? >", |line| { read_and(C_RESV, "se>", "? >", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?; let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?;
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m"); println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m");
let code = SquashGroups.fold_stmt(code); let code = SquashGroups.fold_stmt(code);
@@ -115,37 +118,40 @@ fn live_desugar() -> Result<(), RlError> {
}) })
} }
fn query_type_expression(prj: &mut Project) -> Result<(), RlError> { fn print_strings() {
println!("{}", StringInterner::global());
}
fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ty>", "? >", |line| { read_and(C_RESV, "ty>", "? >", |line| {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
// parse it as a path, and convert the path into a borrowed path // parse it as a path, and convert the path into a borrowed path
let ty = Parser::new(Lexer::new(line)).ty()?.kind; let ty: Ty = Parser::new(Lexer::new(line)).parse()?;
let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj); let id = ty.evaluate(prj, prj.root())?;
pretty_handle(id)?; pretty_handle(id.to_entry(prj))?;
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
fn get_by_id(prj: &mut Project) -> Result<(), RlError> { fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
use cl_parser::parser::Parse;
use cl_structures::index_map::MapIndex; use cl_structures::index_map::MapIndex;
use cl_typeck::handle::DefID; use cl_typeck::handle::Handle;
read_and(C_BYID, "id>", "? >", |line| { read_and(C_BYID, "id>", "? >", |line| {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
let mut parser = Parser::new(Lexer::new(line)); let mut parser = Parser::new(Lexer::new(line));
let def_id = match parser.literal()? { let def_id = match Parse::parse(&mut parser)? {
cl_ast::Literal::Int(int) => int as _, cl_ast::Literal::Int(int) => int as _,
other => Err(format!("Expected integer, got {other}"))?, other => Err(format!("Expected integer, got {other}"))?,
}; };
let mut path = parser.path().unwrap_or_default(); let mut path = parser.parse::<cl_ast::Path>().unwrap_or_default();
path.absolute = false; path.absolute = false;
let Some(handle) = DefID::from_usize(def_id).handle(prj) else { let handle = Handle::from_usize(def_id).to_entry(prj);
return Ok(Response::Deny);
};
print!(" > {{{C_LISTING}{handle}\x1b[0m}}"); print!(" > {{{C_LISTING}{handle}\x1b[0m}}");
if !path.parts.is_empty() { if !path.parts.is_empty() {
@@ -153,71 +159,134 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
} }
println!(); println!();
let (ty, value) = handle.navigate((&path).into()); let Some(entry) = handle.nav(&path.parts) else {
if let (None, None) = (ty, value) {
Err("No results.")? Err("No results.")?
} };
if let Some(t) = ty {
println!("Result in type namespace: {}", t.id()); pretty_handle(entry)?;
pretty_handle(t)?;
}
if let Some(v) = value {
println!("Result in value namespace: {}", v.id());
pretty_handle(v)?;
}
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> { fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
prj.resolve_imports()?; for (id, error) in import(table) {
for id in prj.pool.keys() { eprintln!("{error} in {} ({id})", id.to_entry(table))
resolve(prj, id)?;
} }
println!("Types resolved successfully!"); for handle in table.handle_iter() {
if let Err(error) = handle.to_entry_mut(table).categorize() {
eprintln!("{error}");
}
}
for handle in implement(table) {
eprintln!("Unable to reparent {} ({handle})", handle.to_entry(table))
}
println!("...Resolved!");
Ok(()) Ok(())
} }
fn list_types(prj: &mut Project) { fn list_types(table: &mut Table) {
println!(" name\x1b[30G type"); for handle in table.debug_entry_iter() {
for (idx, key) in prj.pool.keys().enumerate() { let id = handle.id();
let Def { node: Node { vis, kind: source, .. }, .. } = &prj[key]; let kind = handle.kind().unwrap();
let name = match source.as_ref().map(NodeSource::name) { let name = handle.name().unwrap_or("".into());
Some(Some(name)) => name, println!("{id:3}: {name:16}| {kind}: {handle}");
_ => "".into(),
};
print!(
"{idx:3}: {vis}{name}\x1b[30G = {}",
key.handle_unchecked(prj)
);
println!();
} }
} }
fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> { fn import_files(table: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "fi>", "? >", |line| {
let line = line.trim();
if line.is_empty() {
return Ok(Response::Break);
}
let Ok(file) = std::fs::read_to_string(line) else {
for file in std::fs::read_dir(line)? {
println!("{}", file?.path().display())
}
return Ok(Response::Accept);
};
let mut parser = Parser::new(Lexer::new(&file));
let code = match parser.parse() {
Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())),
Err(e) => {
eprintln!("{C_ERROR}{line}:{e}\x1b[0m");
return Ok(Response::Deny);
}
};
Populator::new(table).visit_file(interned(code));
println!("...Imported!");
Ok(Response::Accept)
})
}
fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> {
use std::io::Write; use std::io::Write;
let mut stdout = std::io::stdout().lock(); let mut out = std::io::stdout().lock();
let Some(Def { module, node: Node { vis, .. }, .. }) = handle.get() else { let Some(kind) = entry.kind() else {
return writeln!(stdout, "Invalid handle: {handle}"); return writeln!(out, "{entry}");
}; };
writeln!(stdout, "{C_LISTING}{vis}{handle}\x1b[0m: {}", handle.id())?; write!(out, "{C_LISTING}{kind}")?;
if let Some(parent) = module.parent {
writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {}", handle.with(parent))?; if let Some(name) = entry.name() {
write!(out, " {name}")?;
} }
if !module.types.is_empty() { writeln!(out, "\x1b[0m ({}): {entry}", entry.id())?;
writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?;
for (name, def) in &module.types { if let Some(parent) = entry.parent() {
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? writeln!(
out,
"- {C_LISTING}Parent\x1b[0m: {parent} ({})",
parent.id()
)?;
}
if let Some(span) = entry.span() {
writeln!(
out,
"- {C_LISTING}Span:\x1b[0m ({}, {})",
span.head, span.tail
)?;
}
match entry.meta() {
Some(meta) if !meta.is_empty() => {
writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?;
for meta in meta {
writeln!(out, " - {meta}")?;
}
}
_ => {}
}
if let Some(children) = entry.children() {
writeln!(out, "- {C_LISTING}Children:\x1b[0m")?;
for (name, child) in children {
writeln!(
out,
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
entry.with_id(*child)
)?
} }
} }
if !module.values.is_empty() {
writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?; if let Some(imports) = entry.imports() {
for (name, def) in &module.values { writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?;
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))? for (name, child) in imports {
writeln!(
out,
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
entry.with_id(*child)
)?
} }
} }
write!(stdout, "\x1b[0m")
Ok(())
} }
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File { fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File {
@@ -249,18 +318,11 @@ fn banner() {
); );
} }
/// Keeps leaked references to past ASTs, for posterity:tm: /// Interns a [File](cl_ast::File), returning a static reference to it.
struct TreeManager { fn interned(file: cl_ast::File) -> &'static cl_ast::File {
trees: Vec<&'static cl_ast::File>, use cl_structures::intern::{interned::Interned, typed_interner::TypedInterner};
} static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> =
LazyLock::new(Default::default);
impl TreeManager { Interned::to_ref(&INTERNER.get_or_insert(file))
const fn new() -> Self {
Self { trees: vec![] }
}
fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File {
let ptr = Box::leak(Box::new(tree));
self.trees.push(ptr);
ptr
}
} }

View File

@@ -1,193 +0,0 @@
use crate::{
handle::DefID,
module::Module,
node::{Node, NodeSource},
};
use cl_ast::{Meta, Sym, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Def<'a> {
pub node: Node<'a>,
pub kind: DefKind,
pub module: Module,
}
impl<'a> Def<'a> {
pub fn with_node(node: Node<'a>) -> Self {
Self { node, kind: DefKind::Undecided, module: Default::default() }
}
}
impl Def<'_> {
pub fn name(&self) -> Option<Sym> {
match self.node.kind {
Some(source) => source.name(),
None => None,
}
}
pub fn is_transparent(&self) -> bool {
!matches!(self.kind, DefKind::Type(_))
}
}
mod builder_functions {
use super::*;
impl<'a> Def<'a> {
pub fn set_vis(&mut self, vis: Visibility) -> &mut Self {
self.node.vis = vis;
self
}
pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self {
self.node.meta = meta;
self
}
pub fn set_kind(&mut self, kind: DefKind) -> &mut Self {
self.kind = kind;
self
}
pub fn set_source(&mut self, source: NodeSource<'a>) -> &mut Self {
self.node.kind = Some(source);
self
}
pub fn set_module(&mut self, module: Module) -> &mut Self {
self.module = module;
self
}
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub enum DefKind {
/// An unevaluated definition
#[default]
Undecided,
/// An impl block
Impl(DefID),
/// A use tree, and its parent
Use(DefID),
/// A type, such as a `type`, `struct`, or `enum`
Type(TypeKind),
/// A value, such as a `const`, `static`, or `fn`
Value(ValueKind),
}
/// A [ValueKind] represents an item in the Value Namespace
/// (a component of a [Project](crate::project::Project)).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ValueKind {
Const(DefID),
Static(DefID),
Local(DefID),
Fn(DefID),
}
/// A [TypeKind] represents an item in the Type Namespace
/// (a component of a [Project](crate::project::Project)).
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind {
/// An alias for an already-defined type
Alias(Option<DefID>),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
/// A user-defined aromatic data type
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(u16, DefID),
/// A contiguous view of dynamically sized memory
Slice(DefID),
/// A contiguous view of statically sized memory
Array(DefID, usize),
/// A tuple of existing types
Tuple(Vec<DefID>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: DefID, rety: DefID },
/// The unit type
Empty,
/// The never type
Never,
/// An untyped module
Module,
}
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt {
/// A union-like enum type
Enum(Vec<(Sym, Option<DefID>)>),
/// A C-like enum
CLikeEnum(Vec<(Sym, u128)>),
/// An enum with no fields, which can never be constructed
FieldlessEnum,
/// A structural product type with named members
Struct(Vec<(Sym, Visibility, DefID)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, DefID)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(Sym, DefID)>),
}
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic {
/// An 8-bit signed integer: `#[intrinsic = "i8"]`
I8,
/// A 16-bit signed integer: `#[intrinsic = "i16"]`
I16,
/// A 32-bit signed integer: `#[intrinsic = "i32"]`
I32,
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
I64,
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
// I128,
/// A ptr-len signed integer: `#[intrinsic = "isize"]`
Isize,
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
U8,
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
U16,
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
U32,
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
U64,
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
// U128,
/// A ptr-len unsigned integer: `#[intrinsic = "isize"]`
Usize,
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
Bool,
/// The unicode codepoint type: #[intrinsic = "char"]
Char,
}
impl FromStr for Intrinsic {
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,
"isize" => Intrinsic::Isize,
"u8" => Intrinsic::U8,
"u16" => Intrinsic::U16,
"u32" => Intrinsic::U32,
"u64" => Intrinsic::U64,
"usize" => Intrinsic::Usize,
"bool" => Intrinsic::Bool,
"char" => Intrinsic::Char,
_ => Err(())?,
})
}
}

View File

@@ -0,0 +1,184 @@
//! An [Entry] is an accessor for [nodes](Handle) in a [Table].
//!
//! There are two kinds of entry:
//! - [Entry]: Provides getters for an entry's fields, and an implementation of
//! [Display](std::fmt::Display)
//! - [EntryMut]: Provides setters for an entry's fields, and an [`as_ref`](EntryMut::as_ref) method
//! to demote to an [Entry].
use std::collections::HashMap;
use cl_ast::{Meta, PathPart, Sym};
use cl_structures::span::Span;
use crate::{
handle::Handle,
source::Source,
stage::categorize as cat,
table::{NodeKind, Table},
type_expression::{self as tex, TypeExpression},
type_kind::TypeKind,
};
mod display;
impl Handle {
pub const fn to_entry<'t, 'a>(self, table: &'t Table<'a>) -> Entry<'t, 'a> {
Entry { id: self, table }
}
pub fn to_entry_mut<'t, 'a>(self, table: &'t mut Table<'a>) -> EntryMut<'t, 'a> {
EntryMut { id: self, table }
}
}
#[derive(Debug)]
pub struct Entry<'t, 'a> {
table: &'t Table<'a>,
id: Handle,
}
impl<'t, 'a> Entry<'t, 'a> {
pub const fn new(table: &'t Table<'a>, id: Handle) -> Self {
Self { table, id }
}
pub const fn id(&self) -> Handle {
self.id
}
pub fn inner(&self) -> &Table<'a> {
self.table
}
pub const fn with_id(&self, id: Handle) -> Entry<'_, 'a> {
Self { table: self.table, id }
}
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'_, 'a>> {
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
}
pub const fn root(&self) -> Handle {
self.table.root()
}
pub fn kind(&self) -> Option<&NodeKind> {
self.table.kind(self.id)
}
pub fn parent(&self) -> Option<Entry<'_, 'a>> {
Some(Entry { id: *self.table.parent(self.id)?, ..*self })
}
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
self.table.children(self.id)
}
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> {
self.table.imports(self.id)
}
pub fn ty(&self) -> Option<&TypeKind> {
self.table.ty(self.id)
}
pub fn span(&self) -> Option<&Span> {
self.table.span(self.id)
}
pub fn meta(&self) -> Option<&'a [Meta]> {
self.table.meta(self.id)
}
pub fn source(&self) -> Option<&Source<'a>> {
self.table.source(self.id)
}
pub fn impl_target(&self) -> Option<Entry<'_, 'a>> {
Some(Entry { id: self.table.impl_target(self.id)?, ..*self })
}
pub fn selfty(&self) -> Option<Entry<'_, 'a>> {
Some(Entry { id: self.table.selfty(self.id)?, ..*self })
}
pub fn name(&self) -> Option<Sym> {
self.table.name(self.id)
}
}
#[derive(Debug)]
pub struct EntryMut<'t, 'a> {
table: &'t mut Table<'a>,
id: Handle,
}
impl<'t, 'a> EntryMut<'t, 'a> {
pub fn new(table: &'t mut Table<'a>, id: Handle) -> Self {
Self { table, id }
}
pub fn as_ref(&self) -> Entry<'_, 'a> {
Entry { table: self.table, id: self.id }
}
pub const fn id(&self) -> Handle {
self.id
}
/// Evaluates a [TypeExpression] in this entry's context
pub fn evaluate<Out>(&mut self, ty: &impl TypeExpression<Out>) -> Result<Out, tex::Error> {
let Self { table, id } = self;
ty.evaluate(table, *id)
}
pub fn categorize(&mut self) -> Result<(), cat::Error> {
cat::categorize(self.table, self.id)
}
/// Constructs a new Handle with the provided parent [Handle]
pub fn with_id(&mut self, parent: Handle) -> EntryMut<'_, 'a> {
EntryMut { table: self.table, id: parent }
}
pub fn nav(&mut self, path: &[PathPart]) -> Option<EntryMut<'_, 'a>> {
Some(EntryMut { id: self.table.nav(self.id, path)?, table: self.table })
}
pub fn new_entry(&mut self, kind: NodeKind) -> EntryMut<'_, 'a> {
let id = self.table.new_entry(self.id, kind);
self.with_id(id)
}
pub fn add_child(&mut self, name: Sym, child: Handle) -> Option<Handle> {
self.table.add_child(self.id, name, child)
}
pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> {
self.table.set_ty(self.id, kind)
}
pub fn set_span(&mut self, span: Span) -> Option<Span> {
self.table.set_span(self.id, span)
}
pub fn set_meta(&mut self, meta: &'a [Meta]) -> Option<&'a [Meta]> {
self.table.set_meta(self.id, meta)
}
pub fn set_source(&mut self, source: Source<'a>) -> Option<Source<'a>> {
self.table.set_source(self.id, source)
}
pub fn set_impl_target(&mut self, target: Handle) -> Option<Handle> {
self.table.set_impl_target(self.id, target)
}
pub fn mark_use_item(&mut self) {
self.table.mark_use_item(self.id)
}
pub fn mark_impl_item(&mut self) {
self.table.mark_impl_item(self.id)
}
}

View File

@@ -0,0 +1,100 @@
use super::*;
use crate::{format_utils::*, type_kind::Adt};
use std::fmt::{self, Write};
/// Printing the name of a named type stops infinite recursion
fn write_name_or(h: Entry, f: &mut impl Write) -> fmt::Result {
match h.name() {
Some(name) => write!(f, "{name}"),
None => write!(f, "{h}"),
}
}
impl fmt::Display for Entry<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Some(&kind) = self.kind() else {
return write!(f, "<invalid type: {}>", self.id);
};
if let Some(ty) = self.ty() {
match ty {
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Intrinsic(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(id) => {
f.write_str("&")?;
let h_id = self.with_id(id);
write_name_or(h_id, f)
}
TypeKind::Slice(id) => {
write_name_or(self.with_id(*id), &mut f.delimit_with("[", "]"))
}
&TypeKind::Array(t, cnt) => {
let mut f = f.delimit_with("[", "]");
write_name_or(self.with_id(t), &mut f)?;
write!(f, "; {cnt}")
}
TypeKind::Tuple(ids) => {
let mut f = f.delimit_with("(", ")");
for (index, &id) in ids.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write_name_or(self.with_id(id), &mut f)?;
}
Ok(())
}
TypeKind::FnSig { args, rety } => {
write!(f, "fn {} -> ", self.with_id(*args))?;
write_name_or(self.with_id(*rety), f)
}
TypeKind::Empty => write!(f, "()"),
TypeKind::Never => write!(f, "!"),
TypeKind::Module => write!(f, "module?"),
}
} else {
write!(f, "{kind}")
}
}
}
fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result {
match adt {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
variants.next().map(|(name, def)| {
move |f: &mut Delimit<_>| match def {
Some(def) => {
write!(f, "{name}: ")?;
write_name_or(h.with_id(*def), f)
}
None => write!(f, "{name}"),
}
})
})(f.delimit_with("enum {", "}"))
}
Adt::Struct(members) => {
let mut members = members.iter();
separate(", ", || {
let (name, vis, id) = members.next()?;
Some(move |f: &mut Delimit<_>| {
write!(f, "{vis}{name}: ")?;
write_name_or(h.with_id(*id), f)
})
})(f.delimit_with("struct {", "}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| {
write!(f, "{vis}")?;
write_name_or(h.with_id(*def), f)
})
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct"),
Adt::Union(_) => todo!("Display union types"),
}
}

View File

@@ -1,187 +1,15 @@
use crate::{definition::Def, path::Path, project::Project}; //! A [Handle] uniquely represents an entry in the [Table](crate::table::Table)
use cl_structures::index_map::*; use cl_structures::index_map::*;
// define the index types // define the index types
make_index! { make_index! {
/// Uniquely represents a [Def][1] in the [Def][1] [Pool] /// Uniquely represents an entry in the [Table](crate::table::Table)
/// Handle,
/// [1]: crate::definition::Def
DefID,
} }
/// A handle to a certain [Def] within a [Project] impl std::fmt::Display for Handle {
#[derive(Clone, Copy, Debug)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub struct Handle<'prj, 'code> { self.0.fmt(f)
id: DefID,
prj: &'prj Project<'code>,
}
impl DefID {
/// Constructs a new [Handle] from this DefID and the provided [Project].
pub fn handle<'p, 'c>(self, prj: &'p Project<'c>) -> Option<Handle<'p, 'c>> {
Handle::new(self, prj)
}
/// Constructs a new [Handle] from this DefID and the provided [Project]
pub fn handle_unchecked<'p, 'c>(self, prj: &'p Project<'c>) -> Handle<'p, 'c> {
Handle::new_unchecked(self, prj)
}
}
impl<'p, 'c> Handle<'p, 'c> {
/// Constructs a new Handle from the provided [DefID] and [Project].
/// Returns [Some]\(Handle) if the [DefID] exists in the [Project].
pub fn new(id: DefID, prj: &'p Project<'c>) -> Option<Self> {
prj.pool.get(id).is_some().then_some(Self { id, prj })
}
/// Constructs a new Handle from the provided [DefID] and [Project] without checking membership.
/// Using the handle may cause panics or other unwanted (but defined) behavior.
pub fn new_unchecked(id: DefID, prj: &'p Project<'c>) -> Self {
Self { id, prj }
}
/// Gets the [Def] this handle points to.
pub fn get(self) -> Option<&'p Def<'c>> {
self.prj.pool.get(self.id)
}
pub fn navigate(self, path: Path) -> (Option<Self>, Option<Self>) {
match self.prj.get(path, self.id) {
Some((Some(ty), Some(vl), _)) => (Some(self.with(ty)), Some(self.with(vl))),
Some((_, Some(vl), _)) => (None, Some(self.with(vl))),
Some((Some(ty), _, _)) => (Some(self.with(ty)), None),
_ => (None, None),
}
}
/// Gets the [Project] this handle points to.
pub fn project(self) -> &'p Project<'c> {
self.prj
}
pub fn id(self) -> DefID {
self.id
}
// TODO: get parent, children, etc.
/// Gets a handle to the other ID within the same project
pub fn with(self, id: DefID) -> Self {
Self { id, ..self }
}
}
mod display {
use super::*;
use crate::{definition::*, format_utils::*};
use std::fmt::{self, Display, Write};
impl Display for DefID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Display for Handle<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { id, prj } = *self;
let Some(def) = prj.pool.get(id) else {
return write!(f, "<invalid type: {id}>");
};
// Match the type
match &def.kind {
DefKind::Undecided => write!(f, "<undecided>"),
DefKind::Impl(id) => write!(f, "impl {}", self.with(*id)),
DefKind::Use(id) => write!(f, "use inside {}", self.with(*id)),
DefKind::Type(kind) => match kind {
TypeKind::Alias(None) => write!(f, "type"),
TypeKind::Alias(Some(id)) => write!(f, "{}", self.with(*id)),
TypeKind::Intrinsic(intrinsic) => write!(f, "{intrinsic}"),
TypeKind::Adt(adt) => display_adt(self, adt, f),
TypeKind::Ref(count, id) => {
for _ in 0..*count {
f.write_char('&')?;
}
self.with(*id).fmt(f)
}
TypeKind::Array(id, size) => {
write!(f.delimit_with("[", "]"), "{}; {size}", self.with(*id))
}
TypeKind::Slice(id) => write!(f.delimit_with("[", "]"), "{}", self.with(*id)),
TypeKind::Tuple(ids) => {
let mut ids = ids.iter();
separate(", ", || {
let id = ids.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{}", self.with(*id)))
})(f.delimit_with("(", ")"))
}
TypeKind::FnSig { args, rety } => {
write!(f, "fn {} -> {}", self.with(*args), self.with(*rety))
}
TypeKind::Empty => write!(f, "()"),
TypeKind::Never => write!(f, "!"),
TypeKind::Module => match def.name() {
Some(name) => write!(f, "mod {name}"),
None => write!(f, "mod"),
},
},
DefKind::Value(kind) => match kind {
ValueKind::Const(id) => write!(f, "const {}", self.with(*id)),
ValueKind::Static(id) => write!(f, "static {}", self.with(*id)),
ValueKind::Local(id) => write!(f, "local {}", self.with(*id)),
ValueKind::Fn(id) => write!(f, "{}", self.with(*id)),
},
}
}
}
/// Formats an ADT: a continuation of [Handle::fmt]
fn display_adt(handle: &Handle, adt: &Adt, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match adt {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(",", || {
variants.next().map(|(name, def)| {
move |f: &mut Delimit<_>| match def {
Some(def) => write!(f, "\n{name}: {}", handle.with(*def)),
None => write!(f, "\n{name}"),
}
})
})(f.delimit_with("enum {", "\n}"))
}
Adt::CLikeEnum(variants) => {
let mut variants = variants.iter();
separate(",", || {
let (name, descrim) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}"))
})(f.delimit_with("enum {", "\n}"))
}
Adt::FieldlessEnum => write!(f, "enum"),
Adt::Struct(members) => {
let mut members = members.iter();
separate(",", || {
let (name, vis, id) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{vis}{name}: {}", handle.with(*id)))
})(f.delimit_with("struct {", "\n}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", handle.with(*def)))
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct;"),
Adt::Union(variants) => {
let mut variants = variants.iter();
separate(",", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{name}: {}", handle.with(*def)))
})(f.delimit_with("union {", "\n}"))
}
}
} }
} }

View File

@@ -4,154 +4,71 @@
//! //!
//! This crate is a major work-in-progress. //! This crate is a major work-in-progress.
//! //!
//! # The [Project](project::Project)™ //! # The [Table](table::Table)™
//! Contains [item definitions](definition) and type expression information. //! A directed graph of nodes and their dependencies.
//! //!
//! *Every* definition is itself a module, and can contain arbitrarily nested items //! Contains [item definitions](handle) and [type expression](type_expression) information.
//! as part of the [Module](module::Module) tree.
//! //!
//! The Project keeps track of a *global intern pool* of definitions, which are //! *Every* item is itself a module, and can contain arbitrarily nested items
//! trivially comparable by [DefID](key::DefID). Note that, for item definitions, //! as part of the item graph
//! identical types in different modules DO NOT COMPARE EQUAL under this constraint. //!
//! However, so-called "anonymous" types *do not* follow this rule, as their //! The table, additionally, has some queues for use in external algorithms,
//! definitions are constructed dynamically and ensured to be unique. //! detailed in the [stage] module.
// Note: it's a class invariant that named types are not added
// to the anon-types list. Please keep it that way. ♥ Thanks!
//! //!
//! # Namespaces //! # Namespaces
//! Within a Project, [definitions](definition::Def) are classified into two namespaces: //! Each item in the graph is given its own namespace, which is further separated into
//! //! two distinct parts:
//! ## Type Namespace: //! - Children of an item are direct descendents (i.e. their `parent` is a handle to the item)
//! - Modules //! - Imports of an item are indirect descendents created by `use` or `impl` directives. They are
//! - Structs //! shadowed by Children with the same name.
//! - Enums
//! - Type aliases
//!
//! ## Value Namespace:
//! - Functions
//! - Constants
//! - Static variables
//!
//! There is a *key* distinction between the two namespaces when it comes to
//! [Path](path::Path) traversal: Only items in the Type Namespace will be considered
//! as an intermediate path target. This means items in the Value namespace are
//! entirely *opaque*, and form a one-way barrier. Items outside a Value can be
//! referred to within the Value, but items inside a Value *cannot* be referred to
//! outside the Value.
//! //!
//! # Order of operations: //! # Order of operations:
//! Currently, the process of type resolution goes as follows: //! For order-of-operations information, see the [stage] module.
//!
//! 1. Traverse the AST, [collecting all Items into item definitions](name_collector)
//! 2. Eagerly [resolve `use` imports](use_importer)
//! 3. Traverse all [definitions](definition::Def), and [resolve the types for every
//! item](type_resolver)
//! 4. TODO: Construct a typed AST for expressions, and type-check them
#![warn(clippy::all)] #![warn(clippy::all)]
/*
How do I flesh out modules in an incremental way?
1. Visit all *modules* and create nodes in a module tree for them
- This can be done by holding mutable references to submodules!
Module:
values: Map(name -> Value),
types: Map(name -> Type),
Value: Either
function: { signature: Type, body: FunctionBody }
static: { Mutability, ty: Type, init: ConstEvaluationResult }
const: { ty: Type, init: ConstEvaluationResult }
*/
pub mod handle;
pub mod node;
pub mod definition;
pub mod module;
pub mod path;
pub mod project;
pub mod name_collector;
pub mod use_importer;
pub mod type_resolver;
pub mod inference;
pub(crate) mod format_utils; pub(crate) mod format_utils;
/* pub mod table;
LET THERE BE NOTES: pub mod handle;
/// What is an inference rule? pub mod entry;
/// An inference rule is a specification with a set of predicates and a judgement
/// Let's give every type an ID pub mod source;
struct TypeID(usize);
/// Let's give every type some data: pub mod type_kind;
struct TypeDef<'def> { pub mod type_expression;
name: String,
definition: &'def Item, pub mod stage {
//! Type collection, evaluation, checking, and inference passes.
//!
//! # Order of operations
//! 1. [mod@populate]: Populate the graph with nodes for every named item.
//! 2. [mod@import]: Import the `use` nodes discovered in [Stage 1](populate).
//! 3. [mod@categorize]: Categorize the nodes according to textual type information.
//! - Creates anonymous types (`fn(T) -> U`, `&T`, `[T]`, etc.) as necessary to fill in the
//! type graph
//! - Creates a new struct type for every enum struct-variant.
//! 4. [mod@implement]: Import members of implementation modules into types.
pub use populate::Populator;
/// Stage 1: Populate the graph with nodes.
pub mod populate;
pub use import::import;
/// Stage 2: Import the `use` nodes discovered in Stage 1.
pub mod import;
pub use categorize::categorize;
/// Stage 3: Categorize the nodes according to textual type information.
pub mod categorize;
pub use implement::implement;
/// Stage 4: Import members of `impl` blocks into their corresponding types.
pub mod implement;
// TODO: Make type inference stage 5
// TODO: Use the type information stored in the [table]
pub mod infer;
} }
and store them in a big vector of type descriptions:
struct TypeMap<'def> {
types: Vec<TypeDef<'def>>,
}
// todo: insertion of a type should yield a TypeID
// todo: impl index with TypeID
Let's store type information as either a concrete type or a generic type:
/// The Type struct represents all valid types, and can be trivially equality-compared
pub struct Type {
/// You can only have a pointer chain 65535 pointers long.
ref_depth: u16,
kind: TKind,
}
pub enum TKind {
Concrete(TypeID),
Generic(usize),
}
And assume I can specify a rule based on its inputs and outputs:
Rule {
operation: If,
/// The inputs field is populated by
inputs: [Concrete(BOOL), Generic(0), Generic(0)],
outputs: Generic(0),
/// This rule is compiler-intrinsic!
through: None,
}
Rule {
operation: Add,
inputs: [Concrete(I32), Concrete(I32)],
outputs: Concrete(I32),
/// This rule is not compiler-intrinsic (it is overloaded!)
through: Some(&ImplAddForI32::Add),
}
These rules can be stored in some kind of rule database:
let rules: Hashmap<Operation, Vec<Rule>> {
}
*/

View File

@@ -1,71 +0,0 @@
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
use cl_ast::Sym;
use cl_structures::index_map::MapIndex;
use crate::handle::DefID;
use std::collections::HashMap;
/// A [Module] is a node in the Module Tree (a component of a
/// [Project](crate::project::Project)).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Module {
pub parent: Option<DefID>,
pub types: HashMap<Sym, DefID>,
pub values: HashMap<Sym, DefID>,
pub imports: Vec<DefID>,
}
impl Module {
pub fn new(parent: DefID) -> Self {
Self { parent: Some(parent), ..Default::default() }
}
pub fn with_optional_parent(parent: Option<DefID>) -> Self {
Self { parent, ..Default::default() }
}
pub fn get(&self, name: Sym) -> (Option<DefID>, Option<DefID>) {
(self.get_type(name), self.get_value(name))
}
pub fn get_type(&self, name: Sym) -> Option<DefID> {
self.types.get(&name).copied()
}
pub fn get_value(&self, name: Sym) -> Option<DefID> {
self.values.get(&name).copied()
}
/// Inserts a type with the provided [name](str) and [id](DefID)
pub fn insert_type(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.types.insert(name, id)
}
/// Inserts a value with the provided [name](str) and [id](DefID)
pub fn insert_value(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.values.insert(name, id)
}
}
impl std::fmt::Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self { parent, types, values, imports } = self;
if let Some(parent) = parent {
writeln!(f, "Parent: {}", parent.get())?;
}
for (name, table) in [("Types", types), ("Values", values)] {
if table.is_empty() {
continue;
}
writeln!(f, "{name}:")?;
for (name, id) in table.iter() {
writeln!(f, " {name} => {id}")?;
}
}
if !imports.is_empty() {
write!(f, "Imports:")?;
for id in imports {
write!(f, "{id},")?;
}
}
Ok(())
}
}

View File

@@ -1,218 +0,0 @@
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
use crate::{
definition::{Def, DefKind},
handle::DefID,
module::Module as Mod,
node::{Node, NodeSource},
project::Project as Prj,
};
use cl_ast::{ast_visitor::Visit, *};
use std::mem;
#[derive(Debug)]
pub struct NameCollector<'prj, 'a> {
path: cl_ast::Path,
prj: &'prj mut Prj<'a>,
parent: DefID,
retval: Option<DefID>,
}
impl<'prj, 'a> NameCollector<'prj, 'a> {
/// Constructs a new [NameCollector] out of a [Project](Prj)
pub fn new(prj: &'prj mut Prj<'a>) -> Self {
Self { parent: prj.root, prj, path: Default::default(), retval: None }
}
/// Constructs a new [NameCollector] out of a [Project](Prj) and a parent [DefID]
pub fn with_root(prj: &'prj mut Prj<'a>, parent: DefID) -> Self {
Self { prj, parent, path: Default::default(), retval: None }
}
/// Runs the provided function with the given parent
pub fn with_parent<F, N>(&mut self, parent: DefID, node: N, f: F)
where F: FnOnce(&mut Self, N) {
let parent = mem::replace(&mut self.parent, parent);
f(self, node);
self.parent = parent;
}
/// Extracts the return value from the provided function
pub fn returns<F, N>(&mut self, node: N, f: F) -> Option<DefID>
where F: FnOnce(&mut Self, N) {
let out = self.retval.take();
f(self, node);
mem::replace(&mut self.retval, out)
}
}
impl<'prj, 'a> Visit<'a> for NameCollector<'prj, 'a> {
fn visit_item(&mut self, i: &'a Item) {
let Item { extents: _, attrs, vis, kind } = i;
if let Some(def) = self.returns(kind, Self::visit_item_kind) {
self.prj[def].set_meta(&attrs.meta).set_vis(*vis);
}
}
fn visit_module(&mut self, m: &'a Module) {
let Self { prj, parent, path, retval: _ } = self;
let Module { name, kind } = m;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Module(m))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.path.push(PathPart::Ident(*name));
self.with_parent(id, kind, Self::visit_module_kind);
self.path.pop();
self.retval = Some(id);
}
fn visit_alias(&mut self, a: &'a Alias) {
let Self { prj, parent, path, retval: _ } = self;
let Alias { to: name, from: _ } = a;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Alias(a))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.retval = Some(id);
}
fn visit_enum(&mut self, e: &'a Enum) {
let Self { prj, parent, path, retval: _ } = self;
let Enum { name, kind } = e;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Enum(e))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_enum_kind);
self.retval = Some(id);
}
fn visit_variant(&mut self, v: &'a Variant) {
let Self { path, prj, parent, retval: _ } = self;
let Variant { name, kind } = v;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Variant(v))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_variant_kind);
self.retval = Some(id);
}
fn visit_struct(&mut self, s: &'a Struct) {
let Self { prj, parent, path, retval: _ } = self;
let Struct { name, kind } = s;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Struct(s))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_struct_kind);
self.retval = Some(id);
}
fn visit_const(&mut self, c: &'a Const) {
let Self { prj, parent, path, retval: _ } = self;
let Const { name, ty: _, init } = c;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Const(c))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
self.with_parent(id, &**init, Self::visit_expr);
self.retval = Some(id);
}
fn visit_static(&mut self, s: &'a Static) {
let Self { prj, parent, path, retval: _ } = self;
let Static { name, mutable: _, ty: _, init } = s;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Static(s))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
self.with_parent(id, &**init, Self::visit_expr);
self.retval = Some(id);
}
fn visit_function(&mut self, f: &'a Function) {
let Self { prj, parent, path, retval: _ } = self;
let Function { name, body, .. } = f;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Function(f))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
if let Some(body) = body {
self.with_parent(id, body, Self::visit_block);
}
self.retval = Some(id);
}
fn visit_impl(&mut self, i: &'a Impl) {
let Self { prj, parent, path, retval: _ } = self;
let Impl { target: _, body } = i;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Impl(i))),
};
let id = prj.pool.insert(def);
// items will get reparented after name collection, when target is available
self.with_parent(id, body, Self::visit_file);
self.retval = Some(id);
}
fn visit_use(&mut self, u: &'a Use) {
let Self { prj, parent, path, retval } = self;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Use(*parent),
node: Node::new(path.clone(), Some(NodeSource::Use(u))),
};
let id = prj.pool.insert(def);
prj[*parent].module.imports.push(id);
*retval = Some(id);
}
fn visit_let(&mut self, l: &'a Let) {
let Self { prj, parent, path, retval: _ } = self;
let Let { name, init, .. } = l;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Local(l))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
if let Some(expr) = init {
self.visit_expr(expr)
}
self.retval = Some(id)
}
}

View File

@@ -1,214 +0,0 @@
//! A [Node] contains the [NodeSource] and [Item] metadata for any
//! [Def](crate::definition::Def), as well as the [Path] of the
//! containing [Module].
//!
//! [Node]s are collected by the [Node Sorcerer](sorcerer),
//! an AST visitor that pairs [NodeSource]s with their surrounding
//! context ([Path], [struct@Span], [Meta], [Visibility])
use cl_ast::ast::*;
use cl_structures::span::Span;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Node<'a> {
pub in_path: Path,
pub span: &'a Span,
pub meta: &'a [Meta],
pub vis: Visibility,
pub kind: Option<NodeSource<'a>>,
}
impl<'a> Node<'a> {
pub fn new(path: Path, kind: Option<NodeSource<'a>>) -> Self {
const DUMMY_SPAN: Span = Span::dummy();
Self { in_path: path, span: &DUMMY_SPAN, meta: &[], vis: Visibility::Public, kind }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NodeSource<'a> {
Root,
Module(&'a Module),
Alias(&'a Alias),
Enum(&'a Enum),
Variant(&'a Variant),
Struct(&'a Struct),
Const(&'a Const),
Static(&'a Static),
Function(&'a Function),
Local(&'a Let),
Impl(&'a Impl),
Use(&'a Use),
Ty(&'a TyKind),
}
impl<'a> NodeSource<'a> {
pub fn name(&self) -> Option<Sym> {
match self {
NodeSource::Root => None,
NodeSource::Module(v) => Some(v.name),
NodeSource::Alias(v) => Some(v.to),
NodeSource::Enum(v) => Some(v.name),
NodeSource::Variant(v) => Some(v.name),
NodeSource::Struct(v) => Some(v.name),
NodeSource::Const(v) => Some(v.name),
NodeSource::Static(v) => Some(v.name),
NodeSource::Function(v) => Some(v.name),
NodeSource::Local(l) => Some(l.name),
NodeSource::Impl(_) | NodeSource::Use(_) | NodeSource::Ty(_) => None,
}
}
/// Returns `true` if this [NodeSource] defines a named value
pub fn is_named_value(&self) -> bool {
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
}
/// Returns `true` if this [NodeSource] defines a named type
pub fn is_named_type(&self) -> bool {
matches!(
self,
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
)
}
/// Returns `true` if this [NodeSource] refers to a [Ty] with no name
pub fn is_anon_type(&self) -> bool {
matches!(self, Self::Ty(_))
}
/// Returns `true` if this [NodeSource] refers to an [Impl] block
pub fn is_impl(&self) -> bool {
matches!(self, Self::Impl(_))
}
/// Returns `true` if this [NodeSource] refers to a [Use] import
pub fn is_use_import(&self) -> bool {
matches!(self, Self::Use(_))
}
}
impl fmt::Display for NodeSource<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Root => "🌳 root 🌳".fmt(f),
Self::Module(arg0) => arg0.fmt(f),
Self::Alias(arg0) => arg0.fmt(f),
Self::Enum(arg0) => arg0.fmt(f),
Self::Variant(arg0) => arg0.fmt(f),
Self::Struct(arg0) => arg0.fmt(f),
Self::Const(arg0) => arg0.fmt(f),
Self::Static(arg0) => arg0.fmt(f),
Self::Function(arg0) => arg0.fmt(f),
Self::Impl(arg0) => arg0.fmt(f),
Self::Use(arg0) => arg0.fmt(f),
Self::Ty(arg0) => arg0.fmt(f),
Self::Local(arg0) => arg0.fmt(f),
}
}
}
pub mod sorcerer {
//! An [AST](cl_ast) analysis pass that collects [Node] entries.
use super::{Node, NodeSource};
use cl_ast::{ast::*, ast_visitor::visit::*};
use cl_structures::span::Span;
use std::mem;
/// An AST analysis pass that collects [Node]s
#[derive(Clone, Debug)]
pub struct NodeSorcerer<'a> {
path: Path,
parts: Parts<'a>,
defs: Vec<Node<'a>>,
}
type Parts<'a> = (&'a Span, &'a [Meta], Visibility);
impl<'a> NodeSorcerer<'a> {
pub fn into_defs(self) -> Vec<Node<'a>> {
self.defs
}
fn with_parts<F>(&mut self, s: &'a Span, a: &'a [Meta], v: Visibility, f: F)
where F: FnOnce(&mut Self) {
let parts = mem::replace(&mut self.parts, (s, a, v));
f(self);
self.parts = parts;
}
fn with_only_span<F>(&mut self, span: &'a Span, f: F)
where F: FnOnce(&mut Self) {
self.with_parts(span, &[], Visibility::Public, f)
}
fn push(&mut self, kind: NodeSource<'a>) {
let Self { path, parts, defs } = self;
let (span, meta, vis) = *parts;
defs.push(Node { in_path: path.clone(), span, meta, vis, kind: Some(kind) })
}
}
impl Default for NodeSorcerer<'_> {
fn default() -> Self {
const DPARTS: Parts = (&Span::dummy(), &[], Visibility::Private);
Self {
path: Path { absolute: true, ..Default::default() },
parts: DPARTS,
defs: Default::default(),
}
}
}
impl<'a> Visit<'a> for NodeSorcerer<'a> {
fn visit_module(&mut self, m: &'a Module) {
let Module { name, kind } = m;
self.path.push(PathPart::Ident(*name));
self.visit_module_kind(kind);
self.path.pop();
}
fn visit_item(&mut self, i: &'a Item) {
let Item { extents, attrs, vis, kind } = i;
self.with_parts(extents, &attrs.meta, *vis, |v| {
v.visit_item_kind(kind);
});
}
fn visit_ty(&mut self, t: &'a Ty) {
let Ty { extents, kind } = t;
self.with_only_span(extents, |v| {
v.push(NodeSource::Ty(kind));
v.visit_ty_kind(kind);
});
}
fn visit_stmt(&mut self, s: &'a Stmt) {
let Stmt { extents, kind, semi } = s;
self.with_only_span(extents, |d| {
d.visit_stmt_kind(kind);
d.visit_semi(semi);
})
}
fn visit_item_kind(&mut self, kind: &'a ItemKind) {
match kind {
ItemKind::Module(i) => self.push(NodeSource::Module(i)),
ItemKind::Alias(i) => self.push(NodeSource::Alias(i)),
ItemKind::Enum(i) => self.push(NodeSource::Enum(i)),
ItemKind::Struct(i) => self.push(NodeSource::Struct(i)),
ItemKind::Const(i) => self.push(NodeSource::Const(i)),
ItemKind::Static(i) => self.push(NodeSource::Static(i)),
ItemKind::Function(i) => self.push(NodeSource::Function(i)),
ItemKind::Impl(i) => self.push(NodeSource::Impl(i)),
ItemKind::Use(i) => self.push(NodeSource::Use(i)),
}
or_visit_item_kind(self, kind);
}
fn visit_stmt_kind(&mut self, kind: &'a StmtKind) {
if let StmtKind::Local(l) = kind {
self.push(NodeSource::Local(l))
}
or_visit_stmt_kind(self, kind);
}
}
}

View File

@@ -1,60 +0,0 @@
//! A [Path] is a borrowed view of an [AST Path](AstPath)
use cl_ast::{Path as AstPath, PathPart};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Path<'p> {
pub absolute: bool,
pub parts: &'p [PathPart],
}
impl<'p> Path<'p> {
pub fn new(path: &'p AstPath) -> Self {
let AstPath { absolute, parts } = path;
Self { absolute: *absolute, parts }
}
pub fn relative(self) -> Self {
Self { absolute: false, ..self }
}
pub fn pop_front(self) -> Option<Self> {
let Self { absolute, parts } = self;
Some(Self { absolute, parts: parts.get(1..)? })
}
pub fn front(self) -> Option<Self> {
let Self { absolute, parts } = self;
Some(Self { absolute, parts: parts.get(..1)? })
}
pub fn is_empty(&self) -> bool {
self.parts.is_empty()
}
pub fn len(&self) -> usize {
self.parts.len()
}
pub fn first(&self) -> Option<&PathPart> {
self.parts.first()
}
}
impl<'p> From<&'p AstPath> for Path<'p> {
fn from(value: &'p AstPath) -> Self {
Self::new(value)
}
}
impl AsRef<[PathPart]> for Path<'_> {
fn as_ref(&self) -> &[PathPart] {
self.parts
}
}
impl std::fmt::Display for Path<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const SEPARATOR: &str = "::";
let Self { absolute, parts } = self;
if *absolute {
write!(f, "{SEPARATOR}")?
}
for (idx, part) in parts.iter().enumerate() {
write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
}
Ok(())
}
}

View File

@@ -1,376 +0,0 @@
//! A [Project] contains a tree of [Def]initions, referred to by their [Path]
use crate::{
definition::{Def, DefKind, TypeKind},
handle::DefID,
module,
node::{Node, NodeSource},
path::Path,
};
use cl_ast::PathPart;
use cl_structures::index_map::IndexMap;
use std::{
collections::HashMap,
ops::{Index, IndexMut},
};
use self::evaluate::EvaluableTypeExpression;
#[derive(Clone, Debug)]
pub struct Project<'a> {
pub pool: IndexMap<DefID, Def<'a>>,
/// Stores anonymous tuples, function pointer types, etc.
pub anon_types: HashMap<TypeKind, DefID>,
pub root: DefID,
}
impl Project<'_> {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Project<'_> {
fn default() -> Self {
const ROOT_PATH: cl_ast::Path = cl_ast::Path { absolute: true, parts: Vec::new() };
let mut pool = IndexMap::default();
let root = pool.insert(Def {
module: Default::default(),
kind: DefKind::Type(TypeKind::Module),
node: Node::new(ROOT_PATH, Some(NodeSource::Root)),
});
let never = pool.insert(Def {
module: module::Module::new(root),
kind: DefKind::Type(TypeKind::Never),
node: Node::new(ROOT_PATH, None),
});
let empty = pool.insert(Def {
module: module::Module::new(root),
kind: DefKind::Type(TypeKind::Empty),
node: Node::new(ROOT_PATH, None),
});
let mut anon_types = HashMap::new();
anon_types.insert(TypeKind::Empty, empty);
anon_types.insert(TypeKind::Never, never);
Self { pool, root, anon_types }
}
}
impl<'a> Project<'a> {
pub fn parent_of(&self, module: DefID) -> Option<DefID> {
self[module].module.parent
}
pub fn root_of(&self, module: DefID) -> DefID {
match self.parent_of(module) {
Some(module) => self.root_of(module),
None => module,
}
}
/// Returns the DefID of the Self type within the given DefID's context
pub fn selfty_of(&self, node: DefID) -> Option<DefID> {
match self[node].kind {
DefKind::Impl(id) => Some(id),
DefKind::Type(_) => Some(node),
_ => self.selfty_of(self.parent_of(node)?),
}
}
pub fn get<'p>(
&self,
path: Path<'p>,
within: DefID,
) -> Option<(Option<DefID>, Option<DefID>, Path<'p>)> {
if path.absolute {
return self.get(path.relative(), self.root_of(within));
}
match path.as_ref() {
[PathPart::SuperKw, ..] => self.get(path.pop_front()?, self.parent_of(within)?),
[PathPart::SelfTy, ..] => self.get(path.pop_front()?, self.selfty_of(within)?),
[PathPart::SelfKw, ..] => self.get(path.pop_front()?, within),
[PathPart::Ident(name)] => {
let (ty, val) = self[within].module.get(*name);
// Transparent nodes can be looked through in reverse
if self[within].is_transparent() {
let lookback = self.parent_of(within).and_then(|p| self.get(path, p));
if let Some((subty, subval, path)) = lookback {
return Some((ty.or(subty), val.or(subval), path));
}
}
Some((ty, val, path.pop_front()?))
}
[PathPart::Ident(name), ..] => {
// TODO: This is currently too permissive, and treats undecided nodes as if they're
// always transparent, among other issues.
let (tysub, _, _) = match self[within].is_transparent() {
true => self.get(path.front()?, within)?,
false => (None, None, path),
};
let ty = self[within].module.get_type(*name).or(tysub)?;
self.get(path.pop_front()?, ty)
}
[] => Some((Some(within), None, path)),
}
}
/// Resolves a path within a module tree, finding the innermost module.
/// Returns the remaining path parts.
pub fn get_type<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> {
if path.absolute {
self.get_type(path.relative(), self.root_of(within))
} else if let Some(front) = path.first() {
let module = &self[within].module;
match front {
PathPart::SelfKw => self.get_type(path.pop_front()?, within),
PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?),
PathPart::SelfTy => self.get_type(path.pop_front()?, self.selfty_of(within)?),
PathPart::Ident(name) => match module.types.get(name) {
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
None => Some((within, path)),
},
}
} else {
Some((within, path))
}
}
/// Inserts the type returned by the provided closure iff the TypeKind doesn't already exist
///
/// Assumes `kind` uniquely identifies the type!
pub fn insert_anonymous_type(
&mut self,
kind: TypeKind,
def: impl FnOnce() -> Def<'a>,
) -> DefID {
*(self
.anon_types
.entry(kind)
.or_insert_with(|| self.pool.insert(def())))
}
pub fn evaluate<T>(&mut self, expr: &T, parent: DefID) -> Result<T::Out, String>
where T: EvaluableTypeExpression {
expr.evaluate(self, parent)
}
}
impl<'a> Index<DefID> for Project<'a> {
type Output = Def<'a>;
fn index(&self, index: DefID) -> &Self::Output {
&self.pool[index]
}
}
impl IndexMut<DefID> for Project<'_> {
fn index_mut(&mut self, index: DefID) -> &mut Self::Output {
&mut self.pool[index]
}
}
pub mod evaluate {
//! An [EvaluableTypeExpression] is a component of a type expression tree
//! or an intermediate result of expression evaluation.
use super::*;
use crate::module;
use cl_ast::{Sym, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple};
/// Things that can be evaluated as a type expression
pub trait EvaluableTypeExpression {
/// The result of type expression evaluation
type Out;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String>;
}
impl EvaluableTypeExpression for Ty {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, id: DefID) -> Result<DefID, String> {
self.kind.evaluate(prj, id)
}
}
impl EvaluableTypeExpression for TyKind {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let id = match self {
// TODO: reduce duplication here
TyKind::Never => prj.anon_types[&TypeKind::Never],
TyKind::Empty => prj.anon_types[&TypeKind::Empty],
// TyKind::Path must be looked up explicitly
TyKind::Path(path) => path.evaluate(prj, parent)?,
TyKind::Slice(slice) => slice.evaluate(prj, parent)?,
TyKind::Array(array) => array.evaluate(prj, parent)?,
TyKind::Tuple(tup) => tup.evaluate(prj, parent)?,
TyKind::Ref(tyref) => tyref.evaluate(prj, parent)?,
TyKind::Fn(tyfn) => tyfn.evaluate(prj, parent)?,
};
// println!("{self} => {id:?}");
Ok(id)
}
}
impl EvaluableTypeExpression for Sym {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
prj[parent]
.module
.types
.get(self)
.copied()
.ok_or_else(|| format!("{self} is not a member of {:?}", prj[parent].name()))
}
}
impl EvaluableTypeExpression for TySlice {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let ty = self.ty.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Slice(ty), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Slice(ty)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyArray {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let kind = TypeKind::Array(self.ty.evaluate(prj, parent)?, self.count);
let root = prj.root;
let id = prj.insert_anonymous_type(kind.clone(), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(kind),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyTuple {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let types = self.types.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Tuple(types.clone()), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Tuple(types)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyRef {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let TyRef { count, mutable: _, to } = self;
let to = to.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Ref(*count, to), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Ref(*count, to)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyFn {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let TyFn { args, rety } = self;
let args = args.evaluate(prj, parent)?;
let rety = match rety {
Some(rety) => rety.evaluate(prj, parent)?,
_ => TyKind::Empty.evaluate(prj, parent)?,
};
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::FnSig { args, rety }, || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::FnSig { args, rety }),
});
Ok(id)
}
}
impl EvaluableTypeExpression for cl_ast::Path {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
Path::from(self).evaluate(prj, parent)
}
}
impl EvaluableTypeExpression for PathPart {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
match self {
PathPart::SuperKw => prj
.parent_of(parent)
.ok_or_else(|| "Attempt to get super of root".into()),
PathPart::SelfKw => Ok(parent),
PathPart::SelfTy => prj
.selfty_of(parent)
.ok_or_else(|| "Attempt to get Self outside a Self-able context".into()),
PathPart::Ident(name) => name.evaluate(prj, parent),
}
}
}
impl<'a> EvaluableTypeExpression for Path<'a> {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let (tid, vid, path) = prj.get(*self, parent).ok_or("Failed to traverse path")?;
if !path.is_empty() {
Err(format!("Could not traverse past boundary: {path}"))?;
}
match (tid, vid) {
(Some(ty), _) => Ok(ty),
(None, Some(val)) => Ok(val),
(None, None) => Err(format!("No type or value found at path {self}")),
}
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for [T] {
type Out = Vec<T::Out>;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let mut types = vec![];
for value in self {
types.push(value.evaluate(prj, parent)?)
}
Ok(types)
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Option<T> {
type Out = Option<T::Out>;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
Ok(match self {
Some(v) => Some(v.evaluate(prj, parent)?),
None => None,
})
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Box<T> {
type Out = T::Out;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
self.as_ref().evaluate(prj, parent)
}
}
}

View File

@@ -0,0 +1,87 @@
//! Holds the [Source] of a definition in the AST
use cl_ast::ast::*;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Source<'a> {
Root,
Module(&'a Module),
Alias(&'a Alias),
Enum(&'a Enum),
Variant(&'a Variant),
Struct(&'a Struct),
Const(&'a Const),
Static(&'a Static),
Function(&'a Function),
Local(&'a Let),
Impl(&'a Impl),
Use(&'a Use),
Ty(&'a TyKind),
}
impl Source<'_> {
pub fn name(&self) -> Option<Sym> {
match self {
Source::Root => None,
Source::Module(v) => Some(v.name),
Source::Alias(v) => Some(v.to),
Source::Enum(v) => Some(v.name),
Source::Variant(v) => Some(v.name),
Source::Struct(v) => Some(v.name),
Source::Const(v) => Some(v.name),
Source::Static(v) => Some(v.name),
Source::Function(v) => Some(v.name),
Source::Local(_) => None,
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
}
}
/// Returns `true` if this [Source] defines a named value
pub fn is_named_value(&self) -> bool {
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
}
/// Returns `true` if this [Source] defines a named type
pub fn is_named_type(&self) -> bool {
matches!(
self,
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
)
}
/// Returns `true` if this [Source] refers to a [Ty] with no name
pub fn is_anon_type(&self) -> bool {
matches!(self, Self::Ty(_))
}
/// Returns `true` if this [Source] refers to an [Impl] block
pub fn is_impl(&self) -> bool {
matches!(self, Self::Impl(_))
}
/// Returns `true` if this [Source] refers to a [Use] import
pub fn is_use_import(&self) -> bool {
matches!(self, Self::Use(_))
}
}
impl fmt::Display for Source<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Root => "🌳 root 🌳".fmt(f),
Self::Module(arg0) => arg0.fmt(f),
Self::Alias(arg0) => arg0.fmt(f),
Self::Enum(arg0) => arg0.fmt(f),
Self::Variant(arg0) => arg0.fmt(f),
Self::Struct(arg0) => arg0.fmt(f),
Self::Const(arg0) => arg0.fmt(f),
Self::Static(arg0) => arg0.fmt(f),
Self::Function(arg0) => arg0.fmt(f),
Self::Impl(arg0) => arg0.fmt(f),
Self::Use(arg0) => arg0.fmt(f),
Self::Ty(arg0) => arg0.fmt(f),
Self::Local(arg0) => arg0.fmt(f),
}
}
}

View File

@@ -0,0 +1,229 @@
//! Categorizes an entry in a table according to its embedded type information
use crate::{
handle::Handle,
source::Source,
table::{NodeKind, Table},
type_expression::{Error as TypeEval, TypeExpression},
type_kind::{Adt, TypeKind},
};
use cl_ast::*;
/// Ensures a type entry exists for the provided handle in the table
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) {
let kind =
TypeKind::Intrinsic(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?);
table.set_ty(node, kind);
return Ok(());
}
}
}
let Some(source) = table.source(node) else {
return Ok(());
};
match source {
Source::Root => Ok(()),
Source::Module(_) => Ok(()),
Source::Alias(a) => cat_alias(table, node, a),
Source::Enum(e) => cat_enum(table, node, e),
Source::Variant(_) => Ok(()),
Source::Struct(s) => cat_struct(table, node, s),
Source::Const(c) => cat_const(table, node, c),
Source::Static(s) => cat_static(table, node, s),
Source::Function(f) => cat_function(table, node, f),
Source::Local(l) => cat_local(table, node, l),
Source::Impl(i) => cat_impl(table, node, i),
Source::Use(_) => Ok(()),
Source::Ty(ty) => ty
.evaluate(table, node)
.map_err(|e| Error::TypeEval(e, " while categorizing a type"))
.map(drop),
}
}
fn parent(table: &Table, node: Handle) -> Handle {
table.parent(node).copied().unwrap_or(node)
}
fn cat_alias(table: &mut Table, node: Handle, a: &Alias) -> CatResult<()> {
let parent = parent(table, node);
let kind = match &a.from {
Some(ty) => TypeKind::Instance(
ty.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing an alias"))?,
),
None => TypeKind::Empty,
};
table.set_ty(node, kind);
Ok(())
}
fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> {
let parent = parent(table, node);
let Struct { name: _, kind } = s;
let kind = match kind {
StructKind::Empty => TypeKind::Adt(Adt::UnitStruct),
StructKind::Tuple(types) => {
let mut out = vec![];
for ty in types {
out.push((Visibility::Public, ty.evaluate(table, parent)?))
}
TypeKind::Adt(Adt::TupleStruct(out))
}
StructKind::Struct(members) => {
let mut out = vec![];
for m in members {
out.push(cat_member(table, node, m)?)
}
TypeKind::Adt(Adt::Struct(out))
}
};
table.set_ty(node, kind);
Ok(())
}
fn cat_member(
table: &mut Table,
node: Handle,
m: &StructMember,
) -> CatResult<(Sym, Visibility, Handle)> {
let StructMember { vis, name, ty } = m;
Ok((*name, *vis, ty.evaluate(table, node)?))
}
fn cat_enum<'a>(table: &mut Table<'a>, node: Handle, e: &'a Enum) -> CatResult<()> {
let Enum { name: _, kind } = e;
let kind = match kind {
EnumKind::NoVariants => TypeKind::Adt(Adt::Enum(vec![])),
EnumKind::Variants(variants) => {
let mut out_vars = vec![];
for v in variants {
out_vars.push(cat_variant(table, node, v)?)
}
TypeKind::Adt(Adt::Enum(out_vars))
}
};
table.set_ty(node, kind);
Ok(())
}
fn cat_variant<'a>(
table: &mut Table<'a>,
node: Handle,
v: &'a Variant,
) -> CatResult<(Sym, Option<Handle>)> {
let parent = parent(table, node);
let Variant { name, kind } = v;
match kind {
VariantKind::Plain => Ok((*name, None)),
VariantKind::CLike(c) => todo!("enum-variant constant {c}"),
VariantKind::Tuple(ty) => {
let ty = ty
.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a variant"))?;
Ok((*name, Some(ty)))
}
VariantKind::Struct(members) => {
let mut out = vec![];
for m in members {
out.push(cat_member(table, node, m)?)
}
let kind = TypeKind::Adt(Adt::Struct(out));
let mut h = node.to_entry_mut(table);
let mut variant = h.new_entry(NodeKind::Type);
variant.set_source(Source::Variant(v));
variant.set_ty(kind);
Ok((*name, Some(variant.id())))
}
}
}
fn cat_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance(
c.ty.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a const"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_static(table: &mut Table, node: Handle, s: &Static) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance(
s.ty.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a static"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance(
f.sign
.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a function"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_local(table: &mut Table, node: Handle, l: &Let) -> CatResult<()> {
let parent = parent(table, node);
if let Some(ty) = &l.ty {
let kind = ty
.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a let binding"))?;
table.set_ty(node, TypeKind::Instance(kind));
}
Ok(())
}
fn cat_impl(table: &mut Table, node: Handle, i: &Impl) -> CatResult<()> {
let parent = parent(table, node);
let Impl { target, body: _ } = i;
let target = match target {
ImplKind::Type(t) => t.evaluate(table, parent),
ImplKind::Trait { impl_trait: _, for_type: t } => t.evaluate(table, parent),
}?;
table.set_impl_target(node, target);
Ok(())
}
type CatResult<T> = Result<T, Error>;
#[derive(Clone, Debug)]
pub enum Error {
BadMeta(Meta),
Recursive(Handle),
TypeEval(TypeEval, &'static str),
}
impl From<TypeEval> for Error {
fn from(value: TypeEval) -> Self {
Error::TypeEval(value, "")
}
}
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::TypeEval(e, during) => write!(f, "{e}{during}"),
}
}
}

View File

@@ -0,0 +1,23 @@
use crate::{handle::Handle, table::Table};
pub fn implement(table: &mut Table) -> Vec<Handle> {
let pending = std::mem::take(&mut table.impls);
let mut errors = vec![];
for node in pending {
if let Err(e) = impl_one(table, node) {
errors.push(e);
}
}
errors
}
pub fn impl_one(table: &mut Table, node: Handle) -> Result<(), Handle> {
let Some(target) = table.impl_target(node) else {
Err(node)?
};
let Table { children, imports, .. } = table;
if let Some(children) = children.get(&node) {
imports.entry(target).or_default().extend(children);
}
Ok(())
}

View File

@@ -0,0 +1,158 @@
//! An algorithm for importing external nodes
use crate::{
handle::Handle,
source::Source,
table::{NodeKind, Table},
};
use cl_ast::{PathPart, Sym, Use, UseTree};
use core::slice;
use std::{collections::HashSet, mem};
type Seen = HashSet<Handle>;
pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> {
let pending = mem::take(&mut table.uses);
let mut seen = Seen::new();
let mut failed = vec![];
for import in pending {
let Err(e) = import_one(table, import, &mut seen) else {
continue;
};
if let Error::NotFound(_, _) = e {
table.mark_use_item(import)
}
failed.push((import, e));
}
failed
}
fn import_one<'a>(table: &mut Table<'a>, item: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
if !seen.insert(item) {
return Ok(());
}
let Some(NodeKind::Use) = table.kind(item) else {
Err(Error::ItsNoUse)?
};
let Some(&dst) = table.parent(item) else {
Err(Error::NoParents)?
};
let Some(code) = table.source(item) else {
Err(Error::NoSource)?
};
let &Source::Use(tree) = code else {
Err(Error::BadSource(*code))?
};
let Use { absolute, tree } = tree;
import_tree(
table,
if !absolute { dst } else { table.root() },
dst,
tree,
seen,
)
}
fn import_tree<'a>(
table: &mut Table<'a>,
src: Handle,
dst: Handle,
tree: &UseTree,
seen: &mut Seen,
) -> UseResult<'a, ()> {
match tree {
UseTree::Tree(trees) => trees
.iter()
.try_for_each(|tree| import_tree(table, src, dst, tree, seen)),
UseTree::Path(part, rest) => {
let source = table
.nav(src, slice::from_ref(part))
.ok_or_else(|| Error::NotFound(src, part.clone()))?;
import_tree(table, source, dst, rest, seen)
}
UseTree::Alias(src_name, dst_name) => {
import_name(table, src, src_name, dst, dst_name, seen)
}
UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name, seen),
UseTree::Glob => import_glob(table, src, dst, seen),
}
}
fn import_glob<'a>(
table: &mut Table<'a>,
src: Handle,
dst: Handle,
seen: &mut Seen,
) -> UseResult<'a, ()> {
let Table { children, imports, .. } = table;
if let Some(c) = children.get(&src) {
imports.entry(dst).or_default().extend(c)
}
import_deps(table, src, seen)?;
let Table { imports, .. } = table;
// Importing imports requires some extra work, since we can't `get_many_mut`
if let Some(i) = imports.get(&src) {
let uses: Vec<_> = i.iter().map(|(&k, &v)| (k, v)).collect();
imports.entry(dst).or_default().extend(uses);
}
Ok(())
}
fn import_name<'a>(
table: &mut Table<'a>,
src: Handle,
src_name: &Sym,
dst: Handle,
dst_name: &Sym,
seen: &mut Seen,
) -> UseResult<'a, ()> {
import_deps(table, src, seen)?;
match table.get_by_sym(src, src_name) {
// TODO: check for new imports clobbering existing imports
Some(src_id) => table.add_import(dst, *dst_name, src_id),
None => Err(Error::NotFound(src, PathPart::Ident(*src_name)))?,
};
Ok(())
}
/// Imports the dependencies of this node
fn import_deps<'a>(table: &mut Table<'a>, node: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
if let Some(items) = table.use_items.get(&node) {
let out = items.clone();
for item in out {
import_one(table, item, seen)?;
}
}
Ok(())
}
pub type UseResult<'a, T> = Result<T, Error<'a>>;
#[derive(Debug)]
pub enum Error<'a> {
ItsNoUse,
NoParents,
NoSource,
BadSource(Source<'a>),
NotFound(Handle, PathPart),
}
impl std::fmt::Display for Error<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::ItsNoUse => write!(f, "Entry is not use"),
Error::NoParents => write!(f, "Entry has no parents"),
Error::NoSource => write!(f, "Entry has no source"),
Error::BadSource(s) => write!(f, "Entry incorrectly marked as use item: {s}"),
Error::NotFound(id, part) => write!(f, "Could not traverse {id}::{part}"),
}
}
}

View File

@@ -96,7 +96,7 @@ impl Type {
} }
/// Checks whether there are any unbound type variables in this type. /// Checks whether there are any unbound type variables in this type.
/// ```rust /// ```rust
/// # use cl_typeck::inference::*; /// # use cl_typeck::stage::infer::*;
/// let bool = Type::new_op("bool".into(), &[]); /// let bool = Type::new_op("bool".into(), &[]);
/// let true_v = Type::new_inst(&bool); /// let true_v = Type::new_inst(&bool);
/// let unbound = Type::new_var(); /// let unbound = Type::new_var();
@@ -149,7 +149,7 @@ impl Type {
/// Panics if this type variable's instance field is already borrowed. /// Panics if this type variable's instance field is already borrowed.
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # use cl_typeck::inference::*; /// # use cl_typeck::stage::infer::*;
/// let t_bool = Type::new_op("bool".into(), &[]); /// let t_bool = Type::new_op("bool".into(), &[]);
/// let t_nest = Type::new_inst(&Type::new_inst(&Type::new_inst(&t_bool))); /// let t_nest = Type::new_inst(&Type::new_inst(&Type::new_inst(&t_bool)));
/// let pruned = t_nest.prune(); /// let pruned = t_nest.prune();

View File

@@ -0,0 +1,167 @@
//! The [Populator] populates entries in the sym table, including span info
use crate::{
entry::EntryMut,
handle::Handle,
source::Source,
table::{NodeKind, Table},
};
use cl_ast::{ast_visitor::Visit, ItemKind, Sym};
#[derive(Debug)]
pub struct Populator<'t, 'a> {
inner: EntryMut<'t, 'a>,
name: Option<Sym>, // this is a hack to get around the Visitor interface
}
impl<'t, 'a> Populator<'t, 'a> {
pub fn new(table: &'t mut Table<'a>) -> Self {
Self { inner: table.root_entry_mut(), name: None }
}
/// Constructs a new Populator with the provided parent Handle
pub fn with_id(&mut self, parent: Handle) -> Populator<'_, 'a> {
Populator { inner: self.inner.with_id(parent), name: None }
}
pub fn new_entry(&mut self, kind: NodeKind) -> Populator<'_, 'a> {
Populator { inner: self.inner.new_entry(kind), name: None }
}
pub fn set_name(&mut self, name: Sym) {
self.name = Some(name);
}
}
impl<'a> Visit<'a> for Populator<'_, 'a> {
fn visit_item(&mut self, i: &'a cl_ast::Item) {
let cl_ast::Item { extents, attrs, vis, kind } = i;
// TODO: this, better, better.
let entry_kind = match kind {
ItemKind::Alias(_) => NodeKind::Type,
ItemKind::Enum(_) => NodeKind::Type,
ItemKind::Struct(_) => NodeKind::Type,
ItemKind::Const(_) => NodeKind::Const,
ItemKind::Static(_) => NodeKind::Static,
ItemKind::Function(_) => NodeKind::Function,
ItemKind::Module(_) => NodeKind::Module,
ItemKind::Impl(_) => NodeKind::Impl,
ItemKind::Use(_) => NodeKind::Use,
};
let mut entry = self.new_entry(entry_kind);
entry.inner.set_span(*extents);
entry.inner.set_meta(&attrs.meta);
entry.visit_span(extents);
entry.visit_attrs(attrs);
entry.visit_visibility(vis);
entry.visit_item_kind(kind);
if let (Some(name), child) = (entry.name, entry.inner.id()) {
self.inner.add_child(name, child);
}
}
fn visit_alias(&mut self, a: &'a cl_ast::Alias) {
let cl_ast::Alias { to, from } = a;
self.inner.set_source(Source::Alias(a));
self.set_name(*to);
if let Some(t) = from {
self.visit_ty(t)
}
}
fn visit_const(&mut self, c: &'a cl_ast::Const) {
let cl_ast::Const { name, ty, init } = c;
self.inner.set_source(Source::Const(c));
self.set_name(*name);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_static(&mut self, s: &'a cl_ast::Static) {
let cl_ast::Static { mutable, name, ty, init } = s;
self.inner.set_source(Source::Static(s));
self.set_name(*name);
self.visit_mutability(mutable);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_module(&mut self, m: &'a cl_ast::Module) {
let cl_ast::Module { name, kind } = m;
self.inner.set_source(Source::Module(m));
self.set_name(*name);
self.visit_module_kind(kind);
}
fn visit_function(&mut self, f: &'a cl_ast::Function) {
let cl_ast::Function { name, sign, bind, body } = f;
self.inner.set_source(Source::Function(f));
self.set_name(*name);
self.visit_ty_fn(sign);
bind.iter().for_each(|p| self.visit_param(p));
if let Some(b) = body {
self.visit_block(b)
}
}
fn visit_struct(&mut self, s: &'a cl_ast::Struct) {
let cl_ast::Struct { name, kind } = s;
self.inner.set_source(Source::Struct(s));
self.set_name(*name);
self.visit_struct_kind(kind);
}
fn visit_enum(&mut self, e: &'a cl_ast::Enum) {
let cl_ast::Enum { name, kind } = e;
self.inner.set_source(Source::Enum(e));
self.set_name(*name);
self.visit_enum_kind(kind);
}
fn visit_impl(&mut self, i: &'a cl_ast::Impl) {
let cl_ast::Impl { target, body } = i;
self.inner.set_source(Source::Impl(i));
self.inner.mark_impl_item();
self.visit_impl_kind(target);
self.visit_file(body);
}
fn visit_use(&mut self, u: &'a cl_ast::Use) {
let cl_ast::Use { absolute: _, tree } = u;
self.inner.set_source(Source::Use(u));
self.inner.mark_use_item();
self.visit_use_tree(tree);
}
fn visit_let(&mut self, l: &'a cl_ast::Let) {
let cl_ast::Let { mutable, name: _, ty, init } = l;
let mut entry = self.new_entry(NodeKind::Local);
entry.inner.set_source(Source::Local(l));
// entry.set_name(*name);
entry.visit_mutability(mutable);
if let Some(ty) = ty {
entry.visit_ty(ty);
}
if let Some(init) = init {
entry.visit_expr(init)
}
// let child = entry.inner.id();
// self.inner.add_child(*name, child);
todo!("Pattern destructuring in cl-typeck")
}
}

View File

@@ -0,0 +1,308 @@
//! The [Table] is a monolithic data structure representing everything the type checker
//! knows about a program.
//!
//! Individual nodes in the table can be queried using the [Entry] API ([Table::entry])
//! or modified using the [EntryMut] API ([Table::entry_mut]).
//!
//! # Contents of a "node"
//! Always present:
//! - [NodeKind]: Determines how this node will be treated during the [stages](crate::stage) of
//! compilation
//! - [Parent node](Handle): Arranges this node in the hierarchical graph structure
//!
//! Populated as needed:
//! - Children: An associative array of [names](Sym) to child nodes in the graph. Child nodes are
//! arranged in a *strict* tree structure, with no back edges
//! - Imports: An associative array of [names](Sym) to other nodes in the graph. Not all import
//! nodes are back edges, but all back edges *must be* import nodes.
//! - [Types](TypeKind): Contains type information populated through type checking and inference.
//! Nodes with unpopulated types may be considered type variables in the future.
//! - [Spans][span]: Positional information from the source text. See [cl_structures::span].
//! - [Metas](Meta): Metadata decorators. These may have an effect throughout the compiler.
//! - [Sources](Source): Pointers back into the AST, for future analysis.
//! - Impl Targets: Sparse mapping of `impl` nodes to their corresponding targets.
//! - etc.
//!
//! [span]: struct@Span
use crate::{
entry::{Entry, EntryMut},
handle::Handle,
source::Source,
type_kind::TypeKind,
};
use cl_ast::{Meta, PathPart, Sym};
use cl_structures::{index_map::IndexMap, span::Span};
use std::collections::HashMap;
// TODO: Cycle detection external to this module
/// The table is a monolithic data structure representing everything the type checker
/// knows about a program.
///
/// See [module documentation](self).
#[derive(Debug)]
pub struct Table<'a> {
root: Handle,
/// This is the source of truth for handles
kinds: IndexMap<Handle, NodeKind>,
parents: IndexMap<Handle, Handle>,
pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) use_items: HashMap<Handle, Vec<Handle>>,
types: HashMap<Handle, TypeKind>,
spans: HashMap<Handle, Span>,
metas: HashMap<Handle, &'a [Meta]>,
sources: HashMap<Handle, Source<'a>>,
// code: HashMap<Handle, BasicBlock>, // TODO: lower sources
impl_targets: HashMap<Handle, Handle>,
anon_types: HashMap<TypeKind, Handle>,
// --- Queues for algorithms ---
pub(crate) impls: Vec<Handle>,
pub(crate) uses: Vec<Handle>,
}
impl<'a> Table<'a> {
pub fn new() -> Self {
let mut kinds = IndexMap::new();
let mut parents = IndexMap::new();
let root = kinds.insert(NodeKind::Root);
assert_eq!(root, parents.insert(root));
Self {
root,
kinds,
parents,
children: HashMap::new(),
imports: HashMap::new(),
use_items: HashMap::new(),
types: HashMap::new(),
spans: HashMap::new(),
metas: HashMap::new(),
sources: HashMap::new(),
impl_targets: HashMap::new(),
anon_types: HashMap::new(),
impls: Vec::new(),
uses: Vec::new(),
}
}
pub fn entry(&self, handle: Handle) -> Entry<'_, 'a> {
handle.to_entry(self)
}
pub fn entry_mut(&mut self, handle: Handle) -> EntryMut<'_, 'a> {
handle.to_entry_mut(self)
}
pub fn new_entry(&mut self, parent: Handle, kind: NodeKind) -> Handle {
let entry = self.kinds.insert(kind);
assert_eq!(entry, self.parents.insert(parent));
entry
}
pub fn add_child(&mut self, parent: Handle, name: Sym, child: Handle) -> Option<Handle> {
self.children.entry(parent).or_default().insert(name, child)
}
pub fn add_import(&mut self, parent: Handle, name: Sym, import: Handle) -> Option<Handle> {
self.imports.entry(parent).or_default().insert(name, import)
}
pub fn mark_use_item(&mut self, item: Handle) {
let parent = self.parents[item];
self.use_items.entry(parent).or_default().push(item);
self.uses.push(item);
}
pub fn mark_impl_item(&mut self, item: Handle) {
self.impls.push(item);
}
pub fn handle_iter(&mut self) -> impl Iterator<Item = Handle> {
self.kinds.keys()
}
/// Returns handles to all nodes sequentially by [Entry]
pub fn debug_entry_iter(&self) -> impl Iterator<Item = Entry<'_, 'a>> {
self.kinds.keys().map(|key| key.to_entry(self))
}
/// Gets the [Handle] of an anonymous type with the provided [TypeKind].
/// If not already present, a new one is created.
pub(crate) fn anon_type(&mut self, kind: TypeKind) -> Handle {
if let Some(id) = self.anon_types.get(&kind) {
return *id;
}
let entry = self.new_entry(self.root, NodeKind::Type);
// Anonymous types require a bijective map (anon_types => Def => types)
self.types.insert(entry, kind.clone());
self.anon_types.insert(kind, entry);
entry
}
pub const fn root_entry(&self) -> Entry<'_, 'a> {
self.root.to_entry(self)
}
pub fn root_entry_mut(&mut self) -> crate::entry::EntryMut<'_, 'a> {
self.root.to_entry_mut(self)
}
// --- inherent properties ---
pub const fn root(&self) -> Handle {
self.root
}
pub fn kind(&self, node: Handle) -> Option<&NodeKind> {
self.kinds.get(node)
}
pub fn parent(&self, node: Handle) -> Option<&Handle> {
self.parents.get(node)
}
pub fn children(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
self.children.get(&node)
}
pub fn imports(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
self.imports.get(&node)
}
pub fn ty(&self, node: Handle) -> Option<&TypeKind> {
self.types.get(&node)
}
pub fn span(&self, node: Handle) -> Option<&Span> {
self.spans.get(&node)
}
pub fn meta(&self, node: Handle) -> Option<&'a [Meta]> {
self.metas.get(&node).copied()
}
pub fn source(&self, node: Handle) -> Option<&Source<'a>> {
self.sources.get(&node)
}
pub fn impl_target(&self, node: Handle) -> Option<Handle> {
self.impl_targets.get(&node).copied()
}
pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> {
self.types.insert(node, kind)
}
pub fn set_span(&mut self, node: Handle, span: Span) -> Option<Span> {
self.spans.insert(node, span)
}
pub fn set_meta(&mut self, node: Handle, meta: &'a [Meta]) -> Option<&'a [Meta]> {
self.metas.insert(node, meta)
}
pub fn set_source(&mut self, node: Handle, source: Source<'a>) -> Option<Source<'a>> {
self.sources.insert(node, source)
}
pub fn set_impl_target(&mut self, node: Handle, target: Handle) -> Option<Handle> {
self.impl_targets.insert(node, target)
}
// --- derived properties ---
/// Gets a handle to the local `Self` type, if one exists
pub fn selfty(&self, node: Handle) -> Option<Handle> {
match self.kinds.get(node)? {
NodeKind::Root | NodeKind::Use => None,
NodeKind::Type => Some(node),
NodeKind::Impl => self.impl_target(node),
_ => self.selfty(*self.parent(node)?),
}
}
pub fn name(&self, node: Handle) -> Option<Sym> {
self.source(node).and_then(|s| s.name())
}
pub fn is_transparent(&self, node: Handle) -> bool {
!matches!(
self.kind(node),
None | Some(NodeKind::Root | NodeKind::Module)
)
}
pub fn get_child(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.children.get(&node).and_then(|c| c.get(name)).copied()
}
pub fn get_import(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.imports.get(&node).and_then(|i| i.get(name)).copied()
}
pub fn get_by_sym(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.get_child(node, name)
.or_else(|| self.get_import(node, name))
.or_else(|| {
self.is_transparent(node)
.then(|| {
self.parent(node)
.and_then(|node| self.get_by_sym(*node, name))
})
.flatten()
})
}
/// Does path traversal relative to the provided `node`.
pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option<Handle> {
match path {
[PathPart::SuperKw, rest @ ..] => self.nav(*self.parent(node)?, rest),
[PathPart::SelfKw, rest @ ..] => self.nav(node, rest),
[PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest),
[PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest),
[] => Some(node),
}
}
}
impl Default for Table<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub enum NodeKind {
Root,
Module,
Type,
Const,
Static,
Function,
Local,
Impl,
Use,
}
mod display {
use super::*;
use std::fmt;
impl fmt::Display for NodeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NodeKind::Root => write!(f, "root"),
NodeKind::Module => write!(f, "mod"),
NodeKind::Type => write!(f, "type"),
NodeKind::Const => write!(f, "const"),
NodeKind::Static => write!(f, "static"),
NodeKind::Function => write!(f, "fn"),
NodeKind::Local => write!(f, "local"),
NodeKind::Use => write!(f, "use"),
NodeKind::Impl => write!(f, "impl"),
}
}
}
}

View File

@@ -0,0 +1,131 @@
//! A [TypeExpression] is a [syntactic](cl_ast) representation of a [TypeKind], and is used to
//! construct type bindings in a [Table]'s typing context.
use crate::{handle::Handle, table::Table, type_kind::TypeKind};
use cl_ast::{PathPart, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple};
#[derive(Clone, Debug, PartialEq, Eq)] // TODO: impl Display and Error
pub enum Error {
BadPath { parent: Handle, path: Vec<PathPart> },
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::BadPath { parent, path } => {
write!(f, "No item at path {parent}")?;
for part in path {
write!(f, "::{part}")?;
}
}
}
Ok(())
}
}
/// A [TypeExpression] is a syntactic representation of a [TypeKind], and is used to construct
/// type bindings in a [Table]'s typing context.
pub trait TypeExpression<Out = Handle> {
/// Evaluates a type expression, recursively creating intermediate bindings.
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Out, Error>;
}
impl TypeExpression for Ty {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
self.kind.evaluate(table, node)
}
}
impl TypeExpression for TyKind {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
match self {
TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node),
TyKind::Slice(s) => s.evaluate(table, node),
TyKind::Tuple(t) => t.evaluate(table, node),
TyKind::Ref(r) => r.evaluate(table, node),
TyKind::Fn(f) => f.evaluate(table, node),
}
}
}
impl TypeExpression for cl_ast::Path {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { absolute, parts } = self;
parts.evaluate(table, if *absolute { table.root() } else { node })
}
}
impl TypeExpression for [PathPart] {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
table
.nav(node, self)
.ok_or_else(|| Error::BadPath { parent: node, path: self.to_owned() })
}
}
impl TypeExpression for TyArray {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { ty, count } = self;
let kind = TypeKind::Array(ty.evaluate(table, node)?, *count);
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TySlice {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { ty } = self;
let kind = TypeKind::Slice(ty.evaluate(table, node)?);
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TyTuple {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { types } = self;
let kind = match types.len() {
0 => TypeKind::Empty,
_ => TypeKind::Tuple(types.evaluate(table, node)?),
};
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TyRef {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { mutable: _, count, to } = self;
let mut t = to.evaluate(table, node)?;
for _ in 0..*count {
let kind = TypeKind::Ref(t);
t = table.anon_type(kind)
}
Ok(t)
}
}
impl TypeExpression for TyFn {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { args, rety } = self;
let kind = TypeKind::FnSig {
args: args.evaluate(table, node)?,
rety: match rety {
Some(ty) => ty.evaluate(table, node)?,
None => TyKind::Empty.evaluate(table, node)?,
},
};
Ok(table.anon_type(kind))
}
}
impl<T: TypeExpression<U>, U> TypeExpression<Vec<U>> for [T] {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Vec<U>, Error> {
let mut out = Vec::with_capacity(self.len());
for te in self {
out.push(te.evaluate(table, node)?) // try_collect is unstable
}
Ok(out)
}
}

View File

@@ -0,0 +1,97 @@
//! A [TypeKind] is a node in the [Table](crate::table::Table)'s type graph
use crate::handle::Handle;
use cl_ast::{Sym, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
/// A [TypeKind] represents an item
/// (a component of a [Table](crate::table::Table))
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind {
/// An alias for an already-defined type
Instance(Handle),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
/// A user-defined aromatic data type
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(Handle),
/// A contiguous view of dynamically sized memory
Slice(Handle),
/// A contiguous view of statically sized memory
Array(Handle, usize),
/// A tuple of existing types
Tuple(Vec<Handle>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: Handle, rety: Handle },
/// The unit type
Empty,
/// The never type
Never,
/// An untyped module
Module,
}
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt {
/// A union-like enum type
Enum(Vec<(Sym, Option<Handle>)>),
/// A structural product type with named members
Struct(Vec<(Sym, Visibility, Handle)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, Handle)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(Sym, Handle)>),
}
/// 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 {
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
Bool, // boolean value
Char, // Unicode codepoint
}
// Author's note: the fsize type is a meme
impl FromStr for Intrinsic {
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,
_ => Err(())?,
})
}
}

View File

@@ -1,68 +1,17 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic] //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind}; use super::{Adt, Intrinsic, TypeKind};
use crate::{format_utils::*, node::Node}; use crate::format_utils::*;
use cl_ast::format::FmtAdapter; use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
impl Display for Def<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { module, node: Node { in_path: _, span: _, meta, vis, kind: source }, kind } =
self;
if !meta.is_empty() {
writeln!(f, "#{meta:?}")?;
}
if let Some(source) = source {
if let Some(name) = source.name() {
writeln!(f, "{vis}{name}:")?;
}
writeln!(f.indent(), "source:\n{source}")?;
} else {
writeln!(f, "{vis}: ")?;
}
writeln!(f, "kind: {kind}")?;
write!(f, "module: {module}")
}
}
impl Display for DefKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DefKind::Undecided => write!(f, "undecided"),
DefKind::Impl(id) => write!(f, "impl {id}"),
DefKind::Use(id) => write!(f, "use (inside {id})"),
DefKind::Type(kind) => write!(f, "{kind}"),
DefKind::Value(kind) => write!(f, "{kind}"),
}
}
}
impl std::fmt::Display for ValueKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueKind::Const(id) => write!(f, "const ({id})"),
ValueKind::Static(id) => write!(f, "static ({id})"),
ValueKind::Local(id) => write!(f, "let ({id})"),
ValueKind::Fn(id) => write!(f, "fn def ({id})"),
}
}
}
impl Display for TypeKind { impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
TypeKind::Alias(def) => match def { TypeKind::Instance(def) => write!(f, "alias to #{def}"),
Some(def) => write!(f, "alias to #{def}"),
None => f.write_str("type"),
},
TypeKind::Intrinsic(i) => i.fmt(f), TypeKind::Intrinsic(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f), TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(cnt, def) => { TypeKind::Ref(def) => write!(f, "&{def}"),
for _ in 0..*cnt {
f.write_str("&")?;
}
def.fmt(f)
}
TypeKind::Slice(def) => write!(f, "slice [#{def}]"), TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"), TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"),
TypeKind::Tuple(defs) => { TypeKind::Tuple(defs) => {
@@ -93,14 +42,6 @@ impl Display for Adt {
}) })
})(f.delimit_with("enum {", "}")) })(f.delimit_with("enum {", "}"))
} }
Adt::CLikeEnum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, descrim) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{name} = {descrim}"))
})(f.delimit_with("enum {", "}"))
}
Adt::FieldlessEnum => write!(f, "enum"),
Adt::Struct(members) => { Adt::Struct(members) => {
let mut members = members.iter(); let mut members = members.iter();
separate(", ", || { separate(", ", || {
@@ -134,12 +75,20 @@ impl Display for Intrinsic {
Intrinsic::I16 => f.write_str("i16"), Intrinsic::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"), Intrinsic::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"), Intrinsic::I64 => f.write_str("i64"),
Intrinsic::I128 => f.write_str("i128"),
Intrinsic::Isize => f.write_str("isize"), Intrinsic::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"), Intrinsic::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"), Intrinsic::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"), Intrinsic::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"), Intrinsic::U64 => f.write_str("u64"),
Intrinsic::U128 => f.write_str("u128"),
Intrinsic::Usize => f.write_str("usize"), 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::Bool => f.write_str("bool"),
Intrinsic::Char => f.write_str("char"), Intrinsic::Char => f.write_str("char"),
} }

View File

@@ -1,336 +0,0 @@
//! Performs step 2 of type checking: Evaluating type definitions
use crate::{
definition::{Adt, Def, DefKind, TypeKind, ValueKind},
handle::DefID,
node::{Node, NodeSource},
project::{evaluate::EvaluableTypeExpression, Project as Prj},
};
use cl_ast::*;
/// Evaluate a single ID
pub fn resolve(prj: &mut Prj, id: DefID) -> Result<(), &'static str> {
let Def { node: Node { kind: Some(source), meta, .. }, kind: DefKind::Undecided, .. } = prj[id]
else {
return Ok(());
};
let kind = match &source {
NodeSource::Root => "root",
NodeSource::Alias(_) => "type",
NodeSource::Module(_) => "mod",
NodeSource::Enum(_) => "enum",
NodeSource::Variant(_) => "variant",
NodeSource::Struct(_) => "struct",
NodeSource::Const(_) => "const",
NodeSource::Static(_) => "static",
NodeSource::Function(_) => "fn",
NodeSource::Impl(_) => "impl",
NodeSource::Use(_) => "use",
NodeSource::Local(_) => "let",
NodeSource::Ty(_) => "ty",
};
let name = prj[id].name().unwrap_or("".into());
eprintln!("Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {name}\x1b[0m\" (`{id:?}`)");
for Meta { name, kind } in meta {
if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
prj[id].kind = DefKind::Type(TypeKind::Intrinsic(
s.parse().map_err(|_| "Failed to parse intrinsic")?,
));
}
}
if DefKind::Undecided == prj[id].kind {
prj[id].kind = source.resolve_type(prj, id)?;
}
eprintln!("\x1b[33m=> {}\x1b[0m", prj[id].kind);
Ok(())
}
/// Resolves a given node
pub trait TypeResolvable<'a> {
/// The return type upon success
type Out;
/// Resolves type expressions within this node
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str>;
}
impl<'a> TypeResolvable<'a> for NodeSource<'a> {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
match self {
NodeSource::Root => Ok(DefKind::Type(TypeKind::Module)),
NodeSource::Module(v) => v.resolve_type(prj, id),
NodeSource::Alias(v) => v.resolve_type(prj, id),
NodeSource::Enum(v) => v.resolve_type(prj, id),
NodeSource::Variant(v) => v.resolve_type(prj, id),
NodeSource::Struct(v) => v.resolve_type(prj, id),
NodeSource::Const(v) => v.resolve_type(prj, id),
NodeSource::Static(v) => v.resolve_type(prj, id),
NodeSource::Function(v) => v.resolve_type(prj, id),
NodeSource::Local(v) => v.resolve_type(prj, id),
NodeSource::Impl(v) => v.resolve_type(prj, id),
NodeSource::Use(v) => v.resolve_type(prj, id),
NodeSource::Ty(v) => v.resolve_type(prj, id),
}
}
}
impl<'a> TypeResolvable<'a> for &'a Meta {
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Meta { name, kind } = self;
match (name.as_ref(), kind) {
("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => Ok(DefKind::Type(
TypeKind::Intrinsic(intrinsic.parse().map_err(|_| "unknown intrinsic type")?),
)),
(_, MetaKind::Plain) => Ok(DefKind::Type(TypeKind::Intrinsic(
name.parse().map_err(|_| "Unknown intrinsic type")?,
))),
_ => Err("Unknown meta attribute"),
}
}
}
impl<'a> TypeResolvable<'a> for &'a Module {
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
Ok(DefKind::Type(TypeKind::Module))
}
}
impl<'a> TypeResolvable<'a> for &'a Alias {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let alias = if let Some(ty) = &self.from {
Some(
ty.evaluate(prj, parent)
.or_else(|_| ty.evaluate(prj, id))
.map_err(|_| "Unresolved type in alias")?,
)
} else {
None
};
Ok(DefKind::Type(TypeKind::Alias(alias)))
}
}
impl<'a> TypeResolvable<'a> for &'a Enum {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Enum { name: _, kind } = self;
let EnumKind::Variants(v) = kind else {
return Ok(DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)));
};
let mut fields = vec![];
for Variant { name, kind: _ } in v {
let id = prj[id].module.get_type(*name);
fields.push((*name, id))
}
Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields))))
}
}
impl<'a> TypeResolvable<'a> for &'a Variant {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
// Get the grandparent of this node, for name resolution
let parent = prj.parent_of(id).unwrap_or(id);
let grandparent = prj.parent_of(parent).unwrap_or(parent);
let Variant { name: _, kind } = self;
Ok(DefKind::Type(match kind {
VariantKind::Plain => return Ok(DefKind::Type(TypeKind::Empty)),
VariantKind::CLike(_) => return Ok(DefKind::Undecided),
VariantKind::Tuple(ty) => match &ty.kind {
TyKind::Empty => TypeKind::Tuple(vec![]),
TyKind::Tuple(TyTuple { types }) => {
TypeKind::Tuple(types.evaluate(prj, grandparent).map_err(|e| {
eprintln!("{e}");
""
})?)
}
_ => Err("Unexpected TyKind in tuple variant")?,
},
VariantKind::Struct(members) => {
TypeKind::Adt(Adt::Struct(members.resolve_type(prj, parent)?))
}
}))
}
}
impl<'a> TypeResolvable<'a> for &'a Struct {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Struct { name: _, kind } = self;
Ok(match kind {
StructKind::Empty => DefKind::Type(TypeKind::Empty),
StructKind::Tuple(types) => DefKind::Type(TypeKind::Adt(Adt::TupleStruct({
let mut out = vec![];
for ty in types {
out.push((
Visibility::Public,
ty.evaluate(prj, parent)
.map_err(|_| "Unresolved type in tuple-struct member")?,
));
}
out
}))),
StructKind::Struct(members) => {
DefKind::Type(TypeKind::Adt(Adt::Struct(members.resolve_type(prj, id)?)))
}
})
}
}
impl<'a> TypeResolvable<'a> for &'a StructMember {
type Out = (Sym, Visibility, DefID);
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let StructMember { name, vis, ty } = self;
let ty = ty
.evaluate(prj, parent)
.map_err(|_| "Invalid type while resolving StructMember")?;
Ok((*name, *vis, ty))
}
}
impl<'a> TypeResolvable<'a> for &'a Const {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Const { ty, .. } = self;
let ty = ty
.evaluate(prj, id)
.map_err(|_| "Invalid type while resolving const")?;
Ok(DefKind::Value(ValueKind::Const(ty)))
}
}
impl<'a> TypeResolvable<'a> for &'a Static {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Static { ty, .. } = self;
let ty = ty
.evaluate(prj, parent)
.map_err(|_| "Invalid type while resolving static")?;
Ok(DefKind::Value(ValueKind::Static(ty)))
}
}
impl<'a> TypeResolvable<'a> for &'a Function {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Function { sign, .. } = self;
let sign = sign
.evaluate(prj, parent)
.map_err(|_| "Invalid type in function signature")?;
Ok(DefKind::Value(ValueKind::Fn(sign)))
}
}
impl<'a> TypeResolvable<'a> for &'a Let {
type Out = DefKind;
#[allow(unused)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Let { mutable, name, ty, init } = self;
Ok(DefKind::Undecided)
}
}
impl<'a> TypeResolvable<'a> for &'a Impl {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let target = match &self.target {
ImplKind::Type(t) => t.evaluate(prj, parent),
ImplKind::Trait { for_type, .. } => for_type.evaluate(prj, parent),
}
.map_err(|_| "Unresolved type in impl target")?;
match prj.pool.get_many_mut([id, target]) {
// TODO: Better error handling
Err(_) => Err(concat!(
file!(),
line!(),
column!(),
"id and target are same"
))?,
Ok([id, target]) => {
for (name, def) in &id.module.types {
target.module.insert_type(*name, *def);
}
for (name, def) in &id.module.values {
target.module.insert_value(*name, *def);
}
}
}
Ok(DefKind::Impl(target))
}
}
impl<'a> TypeResolvable<'a> for &'a Use {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
todo!("Resolve types for {self} with ID {id} in {prj:?}")
}
}
impl<'a> TypeResolvable<'a> for &'a TyKind {
type Out = DefKind;
#[allow(unused)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
todo!()
}
}
impl<'a, T> TypeResolvable<'a> for &'a [T]
where &'a T: TypeResolvable<'a>
{
type Out = Vec<<&'a T as TypeResolvable<'a>>::Out>;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let mut members = vec![];
for member in self {
members.push(member.resolve_type(prj, id)?);
}
Ok(members)
}
}
impl<'a, T> TypeResolvable<'a> for Option<&'a T>
where &'a T: TypeResolvable<'a>
{
type Out = Option<<&'a T as TypeResolvable<'a>>::Out>;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
match self {
Some(t) => Some(t.resolve_type(prj, id)).transpose(),
None => Ok(None),
}
}
}

View File

@@ -1,125 +0,0 @@
//! WIP use-item importer. This performs eager import resolution on the AST
//!
//! # TODOs:
//! - [ ] Resolve imports using a graph traversal rather than linear iteration
//! - [ ] Separate imported items from natively declared items
//! - [ ] Separate the use-import pass from the project
//! - [ ] Report errors in a meaningful way
//! - [ ] Lazy import resolution using graph-edge traversal during name lookup?
//! - It doesn't seem to me like the imports in a given scope *can change*.
#![allow(unused)]
use std::fmt::format;
use cl_ast::*;
use crate::{
definition::{Def, DefKind},
handle::DefID,
node::NodeSource,
project::Project,
};
type UseResult = Result<(), String>;
impl<'a> Project<'a> {
pub fn resolve_imports(&mut self) -> UseResult {
for id in self.pool.keys() {
self.visit_def(id)?;
}
Ok(())
}
pub fn visit_def(&mut self, id: DefID) -> UseResult {
let Def { kind, node, module } = &self.pool[id];
if let (DefKind::Use(parent), Some(NodeSource::Use(u))) = (kind, node.kind) {
println!("Importing use item {u}");
self.visit_use(u, *parent);
}
Ok(())
}
pub fn visit_use(&mut self, u: &'a Use, parent: DefID) -> UseResult {
let Use { absolute, tree } = u;
self.visit_use_tree(tree, parent, if *absolute { self.root } else { parent })
}
pub fn visit_use_tree(&mut self, tree: &'a UseTree, parent: DefID, c: DefID) -> UseResult {
match tree {
UseTree::Tree(trees) => {
for tree in trees {
self.visit_use_tree(tree, parent, c)?;
}
}
UseTree::Path(part, rest) => {
let c = self.evaluate(part, c)?;
self.visit_use_tree(rest, parent, c)?;
}
UseTree::Name(name) => self.visit_use_leaf(name, parent, c)?,
UseTree::Alias(from, to) => self.visit_use_alias(from, to, parent, c)?,
UseTree::Glob => self.visit_use_glob(parent, c)?,
}
Ok(())
}
pub fn visit_use_path(&mut self) -> UseResult {
Ok(())
}
pub fn visit_use_leaf(&mut self, name: &'a Sym, parent: DefID, c: DefID) -> UseResult {
self.visit_use_alias(name, name, parent, c)
}
pub fn visit_use_alias(
&mut self,
from: &Sym,
name: &Sym,
parent: DefID,
c: DefID,
) -> UseResult {
let mut imported = false;
let c_mod = &self[c].module;
let (tid, vid) = (
c_mod.types.get(from).copied(),
c_mod.values.get(from).copied(),
);
let parent = &mut self[parent].module;
if let Some(tid) = tid {
parent.types.insert(*name, tid);
imported = true;
}
if let Some(vid) = vid {
parent.values.insert(*name, vid);
imported = true;
}
if imported {
Ok(())
} else {
Err(format!("Identifier {name} not found in module {c}"))
}
}
pub fn visit_use_glob(&mut self, parent: DefID, c: DefID) -> UseResult {
// Loop over all the items in c, and add them as items in the parent
if parent == c {
return Ok(());
}
let [parent, c] = self
.pool
.get_many_mut([parent, c])
.expect("parent and c are not the same");
for (k, v) in &c.module.types {
parent.module.types.entry(*k).or_insert(*v);
}
for (k, v) in &c.module.values {
parent.module.values.entry(*k).or_insert(*v);
}
Ok(())
}
}

View File

@@ -96,7 +96,9 @@ Shift = Factor (ShiftOp Factor )* ;
Factor = Term (FactorOp Term )* ; Factor = Term (FactorOp Term )* ;
Term = Unary (TermOp Unary )* ; Term = Unary (TermOp Unary )* ;
Unary = (UnaryKind)* Member ; Unary = (UnaryKind)* Cast ;
Cast = Member ("as" Ty)? ;
Member = Call (Access)* ; Member = Call (Access)* ;
Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ; Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;

View File

@@ -0,0 +1,17 @@
//! Demonstrates the use of [read_and()]:
//!
//! The provided closure:
//! 1. Takes a line of input (a [String])
//! 2. Performs some calculation (using [FromStr])
//! 3. Returns a [Result] containing a [Response] or an [Err]
use repline::{prebaked::read_and, Response};
use std::{error::Error, str::FromStr};
fn main() -> Result<(), Box<dyn Error>> {
read_and("\x1b[33m", " >", " ?>", |line| {
println!("-> {:?}", f64::from_str(line.trim())?);
Ok(Response::Accept)
})?;
Ok(())
}

View File

@@ -302,7 +302,7 @@ impl<'a> Editor<'a> {
} }
} }
impl<'a, 'e> IntoIterator for &'e Editor<'a> { impl<'e> IntoIterator for &'e Editor<'_> {
type Item = &'e char; type Item = &'e char;
type IntoIter = std::iter::Chain< type IntoIter = std::iter::Chain<
std::collections::vec_deque::Iter<'e, char>, std::collections::vec_deque::Iter<'e, char>,
@@ -313,7 +313,7 @@ impl<'a, 'e> IntoIterator for &'e Editor<'a> {
} }
} }
impl<'a> Display for Editor<'a> { impl Display for Editor<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write; use std::fmt::Write;
for c in self.iter() { for c in self.iter() {

View File

@@ -39,7 +39,7 @@ pub mod chars {
if cont & 0xc0 != 0x80 { if cont & 0xc0 != 0x80 {
return None; return None;
} }
out = out << 6 | (cont & 0x3f); out = (out << 6) | (cont & 0x3f);
} }
Some(char::from_u32(out).ok_or(BadUnicode(out))) Some(char::from_u32(out).ok_or(BadUnicode(out)))
} }

48
sample-code/ascii.cl Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env -S conlang -r false
//! Prints out the characters in the ASCII printable range
//! and the Latin-1 supplement in the format of a hex-dump
fn main () {
ascii()
}
fn n_digit(n: u32) -> char {
(if n > 9 {
('a' as u32) + n - 10
} else {
('0' as u32) + n
}) as char
}
fn in_range(num: u32, start: u32, end: u32) -> bool {
(start <= num) && (num <= end )
}
fn ascii() {
for row in 0..16 {
for col in 0..16 {
if col == 8 {
print(' ')
}
print(n_digit(row), n_digit(col), ' ')
}
print(" │")
for col in 0..16 {
print(ascii_picture(row << 4 | col))
}
println("│")
}
}
// Start of the C0 control pictures region
const CO_CONTROL_PICTURES: u32 = '\u{2400}' as u32;
fn ascii_picture(c: u32) -> char {
if c < ' ' as u32 { // C0
(CO_CONTROL_PICTURES + c) as char
} else if c == 127 { // C0:DEL
'' // SYMBOL_FOR_DELETE
} else if c.in_range(0x7f, 0xa0) { // C1
' '
} else c as char
}

7
sample-code/fib.cl Normal file → Executable file
View File

@@ -1,11 +1,12 @@
#!/usr/bin/env -S conlang -r false
// Calculate Fibonacci numbers // Calculate Fibonacci numbers
fn main() { fn main() {
for num in 0..=30 { for num in 0..=30 {
print("fib(", num, ") = ", fib_iterative(num)) println("fib(", num, ") = ", fibit(num))
} }
for num in 0..=30 { for num in 0..=30 {
print("fib(", num, ") = ", fib(num)) println("fib(", num, ") = ", fib(num))
} }
} }
@@ -17,7 +18,7 @@ fn fib(a: i64) -> i64 {
} }
/// The classic iterative algorithm for fib() /// The classic iterative algorithm for fib()
fn fib_iterative(n: i64) -> i64 { fn fibit(n: i64) -> i64 {
let mut a = 0; let mut a = 0;
let mut b = 1; let mut b = 1;
let mut c = 1; let mut c = 1;

5
sample-code/fizzbuzz.cl Normal file → Executable file
View File

@@ -1,13 +1,14 @@
#!/usr/bin/env -S conlang -r false
// FizzBuzz, using the unstable variadic-`print` builtin // FizzBuzz, using the unstable variadic-`print` builtin
fn main() { fn main() {
fizzbuzz(10, 20) fizzbuzz(0, 30)
} }
// Outputs FizzBuzz for numbers between `start` and `end`, inclusive // Outputs FizzBuzz for numbers between `start` and `end`, inclusive
fn fizzbuzz(start: i128, end: i128) { fn fizzbuzz(start: i128, end: i128) {
for x in start..=end { for x in start..=end {
print(if x % 15 == 0 { println(if x % 15 == 0 {
"FizzBuzz" "FizzBuzz"
} else if 0 == x % 3 { } else if 0 == x % 3 {
"Fizz" "Fizz"

5
sample-code/hello.cl Normal file → Executable file
View File

@@ -1,3 +1,6 @@
#!/usr/bin/env -S conlang -r false
//! Prints "Hello, world!"
fn main() { fn main() {
print("Hello, world!") println("Hello, world!")
} }

5
sample-code/hello.tp Normal file
View File

@@ -0,0 +1,5 @@
// The main function
nasin wan () {
toki_linja("mi toki ale a!")
}

View File

@@ -1,23 +1,40 @@
//! Formats numbers in hexadecimal, octal, or binary //! Formats numbers in hexadecimal, octal, or binary
mod math; mod math;
use math::{min, count_leading_zeroes};
// TODO: casting and/or conversion fn as_digit(n: u32) -> char {
const HEX_LUT: Array = [ (if n > 9 {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', n - 10 + ('a' as u32)
]; } else {
n + ('0' as u32)
}) as char
}
pub fn radix(n: i64, radix: i64) {
fn r_str_radix(n: i64, radix: i64) {
if n != 0 {
r_str_radix(n / radix, radix) + as_digit(n % radix)
} else ""
}
if n == 0 {
"0"
} else if n < 0 {
// TODO: breaks at i64::MIN
"-" + r_str_radix(-n, radix)
} else r_str_radix(n, radix)
}
pub fn hex(n: u64) { pub fn hex(n: u64) {
let out = "0x"; let out = "0x";
for xd in min(count_leading_zeroes(n) / 4, 15)..16 { for xd in min(count_leading_zeroes(n) / 4, 15)..16 {
out += HEX_LUT[(n >> (15 - xd) * 4) & 0xf] out += as_digit((n >> (15 - xd) * 4) & 0xf)
} }
out out
} }
pub fn oct(n: u64) { pub fn oct(n: u64) {
let out = "0o"; let out = "0o";
for xd in min((count_leading_zeroes(n) + 2) / 3, 21)..22 { for xd in min((count_leading_zeroes(n) + 2) / 3, 21)..22 {
out += HEX_LUT[(n >> max(63 - (3 * xd), 0)) & 7] out += as_digit((n >> max(63 - (3 * xd), 0)) & 7)
} }
out out
} }
@@ -25,7 +42,7 @@ pub fn oct(n: u64) {
pub fn bin(n: u64) { pub fn bin(n: u64) {
let out = "0b"; let out = "0b";
for xd in min(count_leading_zeroes(n), 63)..64 { for xd in min(count_leading_zeroes(n), 63)..64 {
out += HEX_LUT[(n >> 63 - xd) & 1] out += as_digit((n >> 63 - xd) & 1)
} }
out out
} }

44
sample-code/letter_frequency.cl Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env -S conlang -r false
// Showcases `get_line` behavior by sorting stdin
fn in_range(this: Ord, min: Ord, max: Ord) -> bool {
min < this && this < max
}
fn frequency(s: str) -> [i32; 128] {
let letters = [0;128];
for letter in s {
if (letter).in_range(' ', letters.len() as char) {
letters[(letter as i32)] += 1;
}
}
letters
}
fn plot_freq(freq: [i32; 128]) -> str {
let buf = "";
for idx in 0..len(freq) {
for n in 0..(freq[idx]) {
buf += idx as char;
}
}
buf
}
const msg: str ="letter_frequency.cl
Computes the frequency of ascii characters in a block of text, and prints it bucket-sorted.
Press Ctrl+D to quit.";
fn main () {
println(msg)
let lines = "";
loop {
let line = get_line();
if line == "" { break() }
lines += line;
}
let freq = frequency(lines)
let plot = plot_freq(freq)
println(plot)
}

18
sample-code/match_test.cl Normal file
View File

@@ -0,0 +1,18 @@
//! This is a Conlang library demonstrating `match`
struct Student {
name: str,
age: i32,
}
fn Student (name: str, age: i32) -> Student {
Student: { name, age }
}
fn match_test(student: Student) {
match student {
Student: { name: "shark", age } => println("Found a shark of ", age, " year(s)"),
Student: { name, age: 22 } => println("Found a 22-year-old named ", name),
Student: { name, age } => println("Found someone named ", name, " of ", age, " year(s)"),
}
}

View File

@@ -1,9 +1,15 @@
//! Useful math functions //! Useful math functions
// FIXME:
// These two functions shouldn't actually be polymorphic, but
// the AST interpreter doesn't know about type annotations
// or operator overloading.
#[generic("T")]
pub fn max(a: T, b: T) -> T { pub fn max(a: T, b: T) -> T {
(if a < b { b } else { a }) (if a < b { b } else { a })
} }
#[generic("T")]
pub fn min(a: T, b: T) -> T { pub fn min(a: T, b: T) -> T {
(if a > b { b } else { a }) (if a > b { b } else { a })
} }

10
sample-code/pona.tp Normal file
View File

@@ -0,0 +1,10 @@
//! toki!
nasin toki_pona(nimi: linja) -> linja {
seme nimi {
"a" => "PARTICLE: Emphasis or emotion",
_ => "",
}
}
kiwen main: nasin(Linja) -> Linja = toki_pona;

37
sample-code/rand.cl Normal file
View File

@@ -0,0 +1,37 @@
//! Pseudo-random number generation using a LFSR algorithm
static state: u64 = 0xdeadbeefdeadbeef;
pub fn seed(seed: u64) {
state = seed;
}
pub fn lfsr_next() {
state ^= state >> 7;
state ^= state << 9;
state ^= state >> 13;
}
/// Returns a pseudorandom byte
pub fn rand() -> u8 {
for _ in 0..8 {
lfsr_next()
}
state & 0xff
}
// Prints a maze out of diagonal box drawing characters, ['', '']
fn mazel(width: u64, height: u64) {
let walls = ['\u{2571}', '\u{2572}'];
rand_rect(width, height, walls)
}
// Prints a rectangle with the provided walls
fn rand_rect(width: u64, height: u64, walls: [char; 2]) {
for _ in 0..height {
for _ in 0..width {
print(walls[rand() % 2])
}
println()
}
}

56
sample-code/sqrt.cl Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env conlang-run
//! Square root approximation, and example applications
/// A really small nonzero number
const EPSILON: f64 = 8.8541878188 / 1000000000000.0;
/// Calcuates the absolute value of a number
fn f64_abs(n: f64) -> f64 {
let n = n as f64
if n < (0.0) { -n } else { n }
}
/// Square root approximation using Newton's method
fn sqrt(n: f64) -> f64 {
let n = n as f64
if n < 0.0 {
return 0.0 / 0.0 // TODO: NaN constant
}
if n == 0.0 {
return 0.0
}
let z = n
loop {
let adj = (z * z - n) / (2.0 * z)
z -= adj
if adj.f64_abs() < EPSILON {
break z;
}
}
}
/// Pythagorean theorem: a² + b² = c²
fn pythag(a: f64, b: f64) -> f64 {
sqrt(a * a + b * b)
}
/// Quadratic formula: (-b ± (b² - 4ac)) / 2a
fn quadratic(a: f64, b: f64, c: f64) -> (f64, f64) {
let a = a as f64; let b = b as f64; let c = c as f64;
(
(-b + sqrt(b * b - 4.0 * a * c)) / 2.0 * a,
(-b - sqrt(b * b - 4.0 * a * c)) / 2.0 * a,
)
}
fn main() {
for i in 0..10 {
println("sqrt(",i,") ≅ ",sqrt(i as f64))
}
println("\nPythagorean Theorem")
println("Hypotenuse of ⊿(5, 12): ", pythag(5.0, 12.0))
println("\nQuadratic formula")
println("Roots of 10x² + 4x - 1: ", quadratic(10.0, 40, -1.0))
}

View File

@@ -1,10 +1,12 @@
//! # The Conlang Standard Library //! # The Conlang Standard Library
pub mod preamble { pub mod preamble {
pub use super::num::*; pub use super::{num::*, str::str};
} }
pub mod num; pub mod num;
pub mod str;
#[cfg("test")] #[cfg("test")]
mod test; mod test;

View File

@@ -18,6 +18,9 @@ pub type i32;
#[intrinsic = "i64"] #[intrinsic = "i64"]
pub type i64; pub type i64;
#[intrinsic = "i128"]
pub type i128;
#[intrinsic = "isize"] #[intrinsic = "isize"]
pub type isize; pub type isize;
@@ -33,9 +36,18 @@ pub type u32;
#[intrinsic = "u64"] #[intrinsic = "u64"]
pub type u64; pub type u64;
#[intrinsic = "u128"]
pub type u128;
#[intrinsic = "usize"] #[intrinsic = "usize"]
pub type usize; pub type usize;
#[intrinsic = "f32"]
pub type f32;
#[intrinsic = "f64"]
pub type f64;
// Contains implementations for (TODO) overloaded operators on num types // Contains implementations for (TODO) overloaded operators on num types
pub mod ops { pub mod ops {
use super::*; use super::*;
@@ -226,6 +238,45 @@ pub mod ops {
} }
} }
impl u128 {
pub const MIN: Self = 0;
pub const MAX: Self = !0;
pub const BIT_WIDTH: u32 = 8;
pub fn default() -> Self {
0
}
pub fn mul(a: Self, b: Self) -> Self {
a * b
}
pub fn div(a: Self, b: Self) -> Self {
a / b
}
pub fn rem(a: Self, b: Self) -> Self {
a % b
}
pub fn add(a: Self, b: Self) -> Self {
a + b
}
pub fn sub(a: Self, b: Self) -> Self {
a - b
}
pub fn shl(a: Self, b: u32) -> Self {
a << b
}
pub fn shr(a: Self, b: u32) -> Self {
a >> b
}
pub fn and(a: Self, b: Self) -> Self {
a & b
}
pub fn or(a: Self, b: Self) -> Self {
a | b
}
pub fn xor(a: Self, b: Self) -> Self {
a ^ b
}
}
impl usize { impl usize {
pub const MIN: Self = __march_ptr_width_unsigned_min(); pub const MIN: Self = __march_ptr_width_unsigned_min();
pub const MAX: Self = __march_ptr_width_unsigned_max(); pub const MAX: Self = __march_ptr_width_unsigned_max();
@@ -421,6 +472,45 @@ pub mod ops {
} }
} }
impl i128 {
pub const MIN: Self = !(1 << 128);
pub const MAX: Self = 1 << 128;
pub const BIT_WIDTH: u32 = 8;
pub fn default() -> Self {
0
}
pub fn mul(a: Self, b: Self) -> Self {
a * b
}
pub fn div(a: Self, b: Self) -> Self {
a / b
}
pub fn rem(a: Self, b: Self) -> Self {
a % b
}
pub fn add(a: Self, b: Self) -> Self {
a + b
}
pub fn sub(a: Self, b: Self) -> Self {
a - b
}
pub fn shl(a: Self, b: u32) -> Self {
a << b
}
pub fn shr(a: Self, b: u32) -> Self {
a >> b
}
pub fn and(a: Self, b: Self) -> Self {
a & b
}
pub fn or(a: Self, b: Self) -> Self {
a | b
}
pub fn xor(a: Self, b: Self) -> Self {
a ^ b
}
}
impl isize { impl isize {
pub const MIN: Self = __march_ptr_width_signed_min(); pub const MIN: Self = __march_ptr_width_signed_min();
pub const MAX: Self = __march_ptr_width_signed_max(); pub const MAX: Self = __march_ptr_width_signed_max();

4
stdlib/std/str.cl Normal file
View File

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

View File

@@ -27,7 +27,7 @@ enum Hodgepodge {
fn noop () -> bool { fn noop () -> bool {
loop if false { loop if false {
} else break loop if false { } else break loop if false {
} else break loop if false { } else break loop if false {
@@ -62,3 +62,18 @@ fn if_else() -> i32 {
} }
// block 4 // block 4
} }
mod horrible_imports {
mod foo {
use super::{bar::*, baz::*};
struct Foo(&Foo, &Bar)
}
mod bar {
use super::{foo::*, baz::*};
struct Bar(&Foo, &Baz)
}
mod baz {
use super::{foo::*, bar::*};
struct Baz(&Foo, &Bar)
}
}