Compare commits
No commits in common. "main" and "new_ast" have entirely different histories.
@ -8,13 +8,14 @@ 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.9"
|
version = "0.0.7"
|
||||||
authors = ["John Breaux <j@soft.fish>"]
|
authors = ["John Breaux <j@soft.fish>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
10
compiler/cl-arena/Cargo.toml
Normal file
10
compiler/cl-arena/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "cl-arena"
|
||||||
|
repository.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
42
compiler/cl-arena/src/dropless_arena/tests.rs
Normal file
42
compiler/cl-arena/src/dropless_arena/tests.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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);
|
||||||
|
}
|
396
compiler/cl-arena/src/lib.rs
Normal file
396
compiler/cl-arena/src/lib.rs
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
//! 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;
|
||||||
|
}
|
61
compiler/cl-arena/src/typed_arena/tests.rs
Normal file
61
compiler/cl-arena/src/typed_arena/tests.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@
|
|||||||
//! - [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::*};
|
||||||
|
|
||||||
@ -37,7 +36,6 @@ pub enum Literal {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
Char(char),
|
Char(char),
|
||||||
Int(u128),
|
Int(u128),
|
||||||
Float(u64),
|
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,12 +346,8 @@ 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`]
|
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
|
||||||
Let(Let),
|
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`])\+
|
||||||
@ -401,43 +395,15 @@ pub enum ExprKind {
|
|||||||
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]
|
/// A local variable declaration [Stmt]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Let {
|
pub struct Let {
|
||||||
pub mutable: Mutability,
|
pub mutable: Mutability,
|
||||||
pub name: Pattern,
|
pub name: Sym,
|
||||||
pub ty: Option<Box<Ty>>,
|
pub ty: Option<Box<Ty>>,
|
||||||
pub init: Option<Box<Expr>>,
|
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 {
|
||||||
@ -520,7 +486,6 @@ pub enum UnaryKind {
|
|||||||
Tilde,
|
Tilde,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cast expression: [`Expr`] `as` [`Ty`]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Cast {
|
pub struct Cast {
|
||||||
pub head: Box<ExprKind>,
|
pub head: Box<ExprKind>,
|
||||||
@ -580,6 +545,7 @@ 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>,
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,6 @@ mod display {
|
|||||||
Literal::Bool(v) => v.fmt(f),
|
Literal::Bool(v) => v.fmt(f),
|
||||||
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
||||||
Literal::Int(v) => v.fmt(f),
|
Literal::Int(v) => v.fmt(f),
|
||||||
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
|
|
||||||
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,9 +414,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 {
|
||||||
ExprKind::Empty => "()".fmt(f),
|
ExprKind::Empty => "()".fmt(f),
|
||||||
ExprKind::Quote(v) => v.fmt(f),
|
|
||||||
ExprKind::Let(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),
|
||||||
@ -444,13 +441,6 @@ mod display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
impl Display for Let {
|
||||||
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;
|
||||||
@ -465,47 +455,6 @@ mod display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
write!(f, "match {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;
|
||||||
@ -666,8 +615,11 @@ 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 { mutable, expr } = self;
|
let Self { count, mutable, expr } = self;
|
||||||
write!(f, "&{mutable}{expr}")
|
for _ in 0..*count {
|
||||||
|
f.write_char('&')?;
|
||||||
|
}
|
||||||
|
write!(f, "{mutable}{expr}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,8 +766,6 @@ mod convert {
|
|||||||
}
|
}
|
||||||
impl From for ExprKind {
|
impl From for ExprKind {
|
||||||
Let => ExprKind::Let,
|
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,
|
||||||
@ -855,50 +805,6 @@ 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 {
|
||||||
@ -924,26 +830,10 @@ 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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,6 @@ 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
|
||||||
}
|
}
|
||||||
@ -229,6 +226,15 @@ 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) }
|
||||||
@ -236,56 +242,6 @@ 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;
|
||||||
@ -363,8 +319,9 @@ pub trait Fold {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
|
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
|
||||||
let AddrOf { mutable, expr } = a;
|
let AddrOf { count, 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)),
|
||||||
}
|
}
|
||||||
@ -427,7 +384,6 @@ 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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -570,9 +526,7 @@ 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::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)),
|
||||||
|
@ -23,7 +23,6 @@ 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;
|
||||||
@ -192,6 +191,17 @@ 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);
|
||||||
@ -200,55 +210,6 @@ 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();
|
||||||
@ -317,7 +278,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 { mutable, expr } = a;
|
let AddrOf { count: _, mutable, expr } = a;
|
||||||
self.visit_mutability(mutable);
|
self.visit_mutability(mutable);
|
||||||
self.visit_expr_kind(expr);
|
self.visit_expr_kind(expr);
|
||||||
}
|
}
|
||||||
@ -378,7 +339,6 @@ 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -490,9 +450,7 @@ 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::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),
|
||||||
|
@ -21,7 +21,7 @@ pub struct Indent<'f, F: Write + ?Sized> {
|
|||||||
f: &'f mut F,
|
f: &'f mut F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Write + ?Sized> Write for Indent<'_, F> {
|
impl<'f, F: Write + ?Sized> Write for Indent<'f, 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: Write + ?Sized> Drop for Delimit<'_, F> {
|
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, 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: Write + ?Sized> Write for Delimit<'_, F> {
|
impl<'f, F: Write + ?Sized> Write for Delimit<'f, 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)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
//! - [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)]
|
||||||
|
@ -1,187 +1,81 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
//! Implementations of built-in functions
|
||||||
|
|
||||||
use crate::{
|
use super::{
|
||||||
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,
|
slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A function built into the interpreter.
|
builtins! {
|
||||||
#[derive(Clone, Copy)]
|
const MISC;
|
||||||
pub struct Builtin {
|
|
||||||
/// An identifier to be used during registration
|
|
||||||
pub name: &'static str,
|
|
||||||
/// The signature, displayed when the builtin is printed
|
|
||||||
pub desc: &'static str,
|
|
||||||
/// The function to be run when called
|
|
||||||
pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn description(&self) -> &'static str {
|
|
||||||
self.desc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 version of the input values
|
||||||
fn dbg(arg) {
|
pub fn dbg<_, args> () -> IResult<ConValue> {
|
||||||
println!("{arg:?}");
|
|
||||||
Ok(arg.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Debug-prints the argument
|
|
||||||
fn dbgp(args @ ..) {
|
|
||||||
let mut out = stdout().lock();
|
let mut out = stdout().lock();
|
||||||
args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
|
for arg in args {
|
||||||
Ok(())
|
writeln!(out, "{arg:?}").ok();
|
||||||
|
}
|
||||||
|
Ok(args.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dumps the environment
|
/// Dumps info from the environment
|
||||||
fn dump() @env {
|
pub fn dump<env, _>() -> IResult<ConValue> {
|
||||||
println!("{env}");
|
println!("{}", *env);
|
||||||
Ok(())
|
Ok(ConValue::Empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn builtins() @env {
|
pub fn len<env, _>(list) -> IResult<ConValue> {
|
||||||
for builtin in env.builtins().values().flatten() {
|
Ok(ConValue::Int(match list {
|
||||||
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::Empty => 0,
|
||||||
ConValue::String(s) => s.chars().count() as _,
|
ConValue::String(s) => s.chars().count() as _,
|
||||||
ConValue::Ref(r) => return len(env, slice::from_ref(r.as_ref())),
|
ConValue::Ref(r) => return len.call(env, slice::from_ref(r.as_ref())),
|
||||||
ConValue::Array(t) => t.len() as _,
|
ConValue::Array(t) => t.len() as _,
|
||||||
ConValue::Tuple(t) => t.len() as _,
|
ConValue::Tuple(t) => t.len() as _,
|
||||||
ConValue::RangeExc(start, end) => (end - start) as _,
|
ConValue::RangeExc(start, end) => (end - start) as _,
|
||||||
ConValue::RangeInc(start, end) => (end - start + 1) as _,
|
ConValue::RangeInc(start, end) => (end - start + 1) as _,
|
||||||
_ => Err(Error::TypeError)?,
|
_ => Err(Error::TypeError)?,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a shark
|
|
||||||
fn shark() {
|
|
||||||
Ok('\u{1f988}')
|
|
||||||
}
|
}
|
||||||
];
|
builtins! {
|
||||||
|
const BINARY;
|
||||||
pub const Math: &[Builtin] = &builtins![
|
|
||||||
/// Multiplication `a * b`
|
/// Multiplication `a * b`
|
||||||
fn mul(lhs, rhs) {
|
pub fn mul(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -190,7 +84,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Division `a / b`
|
/// Division `a / b`
|
||||||
fn div(lhs, rhs) {
|
pub fn div(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -199,7 +93,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remainder `a % b`
|
/// Remainder `a % b`
|
||||||
fn rem(lhs, rhs) {
|
pub fn rem(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -208,7 +102,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Addition `a + b`
|
/// Addition `a + b`
|
||||||
fn add(lhs, rhs) {
|
pub fn add(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -218,7 +112,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Subtraction `a - b`
|
/// Subtraction `a - b`
|
||||||
fn sub(lhs, rhs) {
|
pub fn sub(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -227,7 +121,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shift Left `a << b`
|
/// Shift Left `a << b`
|
||||||
fn shl(lhs, rhs) {
|
pub fn shl(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -236,7 +130,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shift Right `a >> b`
|
/// Shift Right `a >> b`
|
||||||
fn shr(lhs, rhs) {
|
pub fn shr(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -245,7 +139,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise And `a & b`
|
/// Bitwise And `a & b`
|
||||||
fn and(lhs, rhs) {
|
pub fn and(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -255,7 +149,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise Or `a | b`
|
/// Bitwise Or `a | b`
|
||||||
fn or(lhs, rhs) {
|
pub fn or(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -265,7 +159,7 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bitwise Exclusive Or `a ^ b`
|
/// Bitwise Exclusive Or `a ^ b`
|
||||||
fn xor(lhs, rhs) {
|
pub fn xor(lhs, rhs) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -274,34 +168,67 @@ pub const Math: &[Builtin] = &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`
|
||||||
fn range_exc(from, to) {
|
pub fn range_exc(lhs, rhs) -> IResult<ConValue> {
|
||||||
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
|
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
||||||
Err(Error::TypeError)?
|
Err(Error::TypeError)?
|
||||||
};
|
};
|
||||||
Ok(ConValue::RangeExc(from, to))
|
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inclusive Range `a..=b`
|
/// Inclusive Range `a..=b`
|
||||||
fn range_inc(from, to) {
|
pub fn range_inc(lhs, rhs) -> IResult<ConValue> {
|
||||||
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
|
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
||||||
Err(Error::TypeError)?
|
Err(Error::TypeError)?
|
||||||
};
|
};
|
||||||
Ok(ConValue::RangeInc(from, to))
|
Ok(ConValue::RangeInc(lhs, rhs))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
builtins! {
|
||||||
|
const UNARY;
|
||||||
/// Negates the ConValue
|
/// Negates the ConValue
|
||||||
fn neg(tail) {
|
pub fn neg(tail) -> IResult<ConValue> {
|
||||||
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
|
||||||
fn not(tail) {
|
pub fn not(tail) -> IResult<ConValue> {
|
||||||
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),
|
||||||
@ -310,23 +237,62 @@ pub const Math: &[Builtin] = &builtins![
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares two values
|
pub fn deref(tail) -> IResult<ConValue> {
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
//! Values in the dynamically typed AST interpreter.
|
//! Values in the dynamically typed AST interpreter.
|
||||||
//!
|
//!
|
||||||
//! The most permanent fix is a temporary one.
|
//! The most permanent fix is a temporary one.
|
||||||
use cl_ast::{format::FmtAdapter, ExprKind, Sym};
|
use cl_ast::Sym;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
builtin::Builtin,
|
|
||||||
error::{Error, IResult},
|
error::{Error, IResult},
|
||||||
function::Function, Callable, Environment,
|
function::Function,
|
||||||
|
BuiltIn, Callable, Environment,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, ops::*, rc::Rc};
|
use std::{ops::*, rc::Rc};
|
||||||
|
|
||||||
type Integer = isize;
|
type Integer = isize;
|
||||||
|
|
||||||
@ -20,8 +20,6 @@ pub enum ConValue {
|
|||||||
Empty,
|
Empty,
|
||||||
/// An integer
|
/// An integer
|
||||||
Int(Integer),
|
Int(Integer),
|
||||||
/// A floating point number
|
|
||||||
Float(f64),
|
|
||||||
/// A boolean
|
/// A boolean
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
/// A unicode character
|
/// A unicode character
|
||||||
@ -31,25 +29,18 @@ pub enum ConValue {
|
|||||||
/// A reference
|
/// A reference
|
||||||
Ref(Rc<ConValue>),
|
Ref(Rc<ConValue>),
|
||||||
/// An Array
|
/// An Array
|
||||||
Array(Box<[ConValue]>),
|
Array(Rc<[ConValue]>),
|
||||||
/// A tuple
|
/// A tuple
|
||||||
Tuple(Box<[ConValue]>),
|
Tuple(Rc<[ConValue]>),
|
||||||
/// An exclusive range
|
/// An exclusive range
|
||||||
RangeExc(Integer, Integer),
|
RangeExc(Integer, Integer),
|
||||||
/// An inclusive range
|
/// An inclusive range
|
||||||
RangeInc(Integer, Integer),
|
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
|
/// A callable thing
|
||||||
Function(Rc<Function>),
|
Function(Function),
|
||||||
/// A built-in function
|
/// A built-in function
|
||||||
Builtin(&'static Builtin),
|
BuiltIn(&'static dyn BuiltIn),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConValue {
|
impl ConValue {
|
||||||
/// Gets whether the current value is true or false
|
/// Gets whether the current value is true or false
|
||||||
pub fn truthy(&self) -> IResult<bool> {
|
pub fn truthy(&self) -> IResult<bool> {
|
||||||
@ -62,7 +53,7 @@ impl ConValue {
|
|||||||
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
||||||
Err(Error::TypeError)?
|
Err(Error::TypeError)?
|
||||||
};
|
};
|
||||||
Ok(Self::RangeExc(a, b))
|
Ok(Self::RangeExc(a, b.saturating_sub(1)))
|
||||||
}
|
}
|
||||||
pub fn range_inc(self, other: Self) -> IResult<Self> {
|
pub fn range_inc(self, other: Self) -> IResult<Self> {
|
||||||
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
||||||
@ -113,14 +104,14 @@ impl Callable for ConValue {
|
|||||||
fn name(&self) -> Sym {
|
fn name(&self) -> Sym {
|
||||||
match self {
|
match self {
|
||||||
ConValue::Function(func) => func.name(),
|
ConValue::Function(func) => func.name(),
|
||||||
ConValue::Builtin(func) => func.name(),
|
ConValue::BuiltIn(func) => func.name(),
|
||||||
_ => "".into(),
|
_ => "".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(func) => func.call(interpreter, args),
|
Self::Function(func) => func.call(interpreter, args),
|
||||||
Self::Builtin(func) => func.call(interpreter, args),
|
Self::BuiltIn(func) => func.call(interpreter, args),
|
||||||
_ => Err(Error::NotCallable(self.clone())),
|
_ => Err(Error::NotCallable(self.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +124,6 @@ macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
|||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
||||||
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
|
(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::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
|
||||||
(Self::Char(a), Self::Char(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)),
|
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
@ -160,17 +150,15 @@ impl From<&Sym> for ConValue {
|
|||||||
}
|
}
|
||||||
from! {
|
from! {
|
||||||
Integer => ConValue::Int,
|
Integer => ConValue::Int,
|
||||||
f64 => ConValue::Float,
|
|
||||||
bool => ConValue::Bool,
|
bool => ConValue::Bool,
|
||||||
char => ConValue::Char,
|
char => ConValue::Char,
|
||||||
Sym => ConValue::String,
|
Sym => ConValue::String,
|
||||||
&str => ConValue::String,
|
&str => ConValue::String,
|
||||||
String => ConValue::String,
|
String => ConValue::String,
|
||||||
Rc<str> => ConValue::String,
|
Rc<str> => ConValue::String,
|
||||||
ExprKind => ConValue::Quote,
|
|
||||||
Function => ConValue::Function,
|
Function => ConValue::Function,
|
||||||
Vec<ConValue> => ConValue::Tuple,
|
Vec<ConValue> => ConValue::Tuple,
|
||||||
&'static Builtin => ConValue::Builtin,
|
&'static dyn BuiltIn => ConValue::BuiltIn,
|
||||||
}
|
}
|
||||||
impl From<()> for ConValue {
|
impl From<()> for ConValue {
|
||||||
fn from(_: ()) -> Self {
|
fn from(_: ()) -> Self {
|
||||||
@ -201,8 +189,7 @@ ops! {
|
|||||||
Add: add = [
|
Add: add = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
|
(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.to_string()).into(),
|
||||||
(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::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
|
||||||
(ConValue::Char(a), ConValue::Char(b)) => {
|
(ConValue::Char(a), ConValue::Char(b)) => {
|
||||||
ConValue::String([a, b].into_iter().collect::<String>().into())
|
ConValue::String([a, b].into_iter().collect::<String>().into())
|
||||||
@ -232,21 +219,18 @@ ops! {
|
|||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
|
||||||
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
||||||
})),
|
})),
|
||||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
|
|
||||||
_ => Err(Error::TypeError)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Mul: mul = [
|
Mul: mul = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
|
(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)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Rem: rem = [
|
Rem: rem = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
|
||||||
println!("Warning: Divide by zero in {a} % {b}"); a
|
eprintln!("Warning: Divide by zero in {a} % {b}"); a
|
||||||
})),
|
})),
|
||||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
|
|
||||||
_ => Err(Error::TypeError)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Shl: shl = [
|
Shl: shl = [
|
||||||
@ -262,7 +246,6 @@ ops! {
|
|||||||
Sub: sub = [
|
Sub: sub = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
|
(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)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -271,7 +254,6 @@ impl std::fmt::Display for ConValue {
|
|||||||
match self {
|
match self {
|
||||||
ConValue::Empty => "Empty".fmt(f),
|
ConValue::Empty => "Empty".fmt(f),
|
||||||
ConValue::Int(v) => v.fmt(f),
|
ConValue::Int(v) => v.fmt(f),
|
||||||
ConValue::Float(v) => v.fmt(f),
|
|
||||||
ConValue::Bool(v) => v.fmt(f),
|
ConValue::Bool(v) => v.fmt(f),
|
||||||
ConValue::Char(v) => v.fmt(f),
|
ConValue::Char(v) => v.fmt(f),
|
||||||
ConValue::String(v) => v.fmt(f),
|
ConValue::String(v) => v.fmt(f),
|
||||||
@ -298,37 +280,10 @@ impl std::fmt::Display for ConValue {
|
|||||||
}
|
}
|
||||||
')'.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) => {
|
ConValue::Function(func) => {
|
||||||
write!(f, "{}", func.decl())
|
write!(f, "{}", func.decl())
|
||||||
}
|
}
|
||||||
ConValue::Builtin(func) => {
|
ConValue::BuiltIn(func) => {
|
||||||
write!(f, "{}", func.description())
|
write!(f, "{}", func.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
//! Lexical and non-lexical scoping for variables
|
//! Lexical and non-lexical scoping for variables
|
||||||
|
|
||||||
use crate::builtin::Builtin;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
builtin::{Builtins, Math},
|
builtin::{BINARY, MISC, RANGE, UNARY},
|
||||||
convalue::ConValue,
|
convalue::ConValue,
|
||||||
error::{Error, IResult},
|
error::{Error, IResult},
|
||||||
function::Function,
|
function::Function,
|
||||||
Callable, Interpret,
|
BuiltIn, Callable, Interpret,
|
||||||
};
|
};
|
||||||
use cl_ast::{Function as FnDecl, Sym};
|
use cl_ast::{Function as FnDecl, Sym};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
type StackFrame = HashMap<Sym, Option<ConValue>>;
|
||||||
@ -22,21 +18,12 @@ type StackFrame = HashMap<Sym, Option<ConValue>>;
|
|||||||
/// Implements a nested lexical scope
|
/// Implements a nested lexical scope
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
builtin: StackFrame,
|
|
||||||
global: Vec<(StackFrame, &'static str)>,
|
|
||||||
frames: Vec<(StackFrame, &'static str)>,
|
frames: Vec<(StackFrame, &'static str)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Environment {
|
impl Display for Environment {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for (frame, name) in self
|
for (frame, name) in self.frames.iter().rev() {
|
||||||
.global
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.take(2)
|
|
||||||
.rev()
|
|
||||||
.chain(self.frames.iter())
|
|
||||||
{
|
|
||||||
writeln!(f, "--- {name} ---")?;
|
writeln!(f, "--- {name} ---")?;
|
||||||
for (var, val) in frame {
|
for (var, val) in frame {
|
||||||
write!(f, "{var}: ")?;
|
write!(f, "{var}: ")?;
|
||||||
@ -52,17 +39,18 @@ impl Display for Environment {
|
|||||||
impl Default for Environment {
|
impl Default for Environment {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
builtin: to_hashmap(Builtins.iter().chain(Math.iter())),
|
frames: vec![
|
||||||
global: vec![(HashMap::new(), "globals")],
|
(to_hashmap(RANGE), "range ops"),
|
||||||
frames: vec![],
|
(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>> {
|
||||||
fn to_hashmap(from: impl IntoIterator<Item = &'static Builtin>) -> HashMap<Sym, Option<ConValue>> {
|
from.iter().map(|&v| (v.name(), Some(v.into()))).collect()
|
||||||
from.into_iter()
|
|
||||||
.map(|v| (v.name(), Some(v.into())))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
@ -70,35 +58,8 @@ impl Environment {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
/// Creates an [Environment] with no [builtins](super::builtin)
|
/// Creates an [Environment] with no [builtins](super::builtin)
|
||||||
pub fn no_builtins() -> Self {
|
pub fn no_builtins(name: &'static str) -> Self {
|
||||||
Self {
|
Self { frames: vec![(Default::default(), name)] }
|
||||||
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> {
|
|
||||||
self.frames.pop().map(|f| f.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
|
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
|
||||||
@ -127,12 +88,7 @@ impl Environment {
|
|||||||
return Ok(var);
|
return Ok(var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (frame, _) in self.global.iter_mut().rev() {
|
Err(Error::NotDefined(id))
|
||||||
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.
|
/// Resolves a variable immutably.
|
||||||
///
|
///
|
||||||
@ -145,50 +101,21 @@ impl Environment {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (frame, _) in self.global.iter().rev() {
|
Err(Error::NotDefined(id))
|
||||||
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]
|
/// Inserts a new [ConValue] into this [Environment]
|
||||||
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
|
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
|
||||||
if let Some((frame, _)) = self.frames.last_mut() {
|
if let Some((frame, _)) = self.frames.last_mut() {
|
||||||
frame.insert(id, value);
|
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]
|
/// A convenience function for registering a [FnDecl] as a [Function]
|
||||||
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
||||||
let FnDecl { name, .. } = decl;
|
let FnDecl { name, .. } = decl;
|
||||||
let (name, function) = (name, Rc::new(Function::new(decl)));
|
let (name, function) = (name, Some(Function::new(decl).into()));
|
||||||
if let Some((frame, _)) = self.frames.last_mut() {
|
if let Some((frame, _)) = self.frames.last_mut() {
|
||||||
frame.insert(*name, Some(ConValue::Function(function.clone())));
|
frame.insert(*name, function);
|
||||||
} 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +130,9 @@ impl Environment {
|
|||||||
/// Exits the scope, destroying all local variables and
|
/// Exits the scope, destroying all local variables and
|
||||||
/// returning the outer scope, if there is one
|
/// returning the outer scope, if there is one
|
||||||
fn exit(&mut self) -> &mut Self {
|
fn exit(&mut self) -> &mut Self {
|
||||||
|
if self.frames.len() > 2 {
|
||||||
self.frames.pop();
|
self.frames.pop();
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,18 +147,18 @@ impl<'scope> Frame<'scope> {
|
|||||||
Self { scope: scope.enter(name) }
|
Self { scope: scope.enter(name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Deref for Frame<'_> {
|
impl<'scope> Deref for Frame<'scope> {
|
||||||
type Target = Environment;
|
type Target = Environment;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.scope
|
self.scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DerefMut for Frame<'_> {
|
impl<'scope> DerefMut for Frame<'scope> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.scope
|
self.scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Frame<'_> {
|
impl<'scope> Drop for Frame<'scope> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.scope.exit();
|
self.scope.exit();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
|
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
|
||||||
|
|
||||||
use cl_ast::{Pattern, Sym};
|
use cl_ast::Sym;
|
||||||
|
|
||||||
use super::convalue::ConValue;
|
use super::convalue::ConValue;
|
||||||
|
|
||||||
@ -39,13 +39,11 @@ pub enum Error {
|
|||||||
/// A value was called, but is not callable
|
/// A value was called, but is not callable
|
||||||
NotCallable(ConValue),
|
NotCallable(ConValue),
|
||||||
/// A function was called with the wrong number of arguments
|
/// A function was called with the wrong number of arguments
|
||||||
ArgNumber { want: usize, got: usize },
|
ArgNumber {
|
||||||
/// A pattern failed to match
|
want: usize,
|
||||||
PatFailed(Box<Pattern>),
|
got: usize,
|
||||||
/// Fell through a non-exhaustive match
|
},
|
||||||
MatchNonexhaustive,
|
Outlined(Sym),
|
||||||
/// Error produced by a Builtin
|
|
||||||
BuiltinDebug(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
@ -85,13 +83,9 @@ impl std::fmt::Display for Error {
|
|||||||
if *want == 1 { "" } else { "s" }
|
if *want == 1 { "" } else { "s" }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Error::PatFailed(pattern) => {
|
Error::Outlined(name) => {
|
||||||
write!(f, "Failed to match pattern {pattern}")
|
write!(f, "Module {name} specified, but not imported.")
|
||||||
}
|
}
|
||||||
Error::MatchNonexhaustive => {
|
|
||||||
write!(f, "Fell through a non-exhaustive match expression!")
|
|
||||||
}
|
|
||||||
Error::BuiltinDebug(s) => write!(f, "DEBUG: {s}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,24 @@
|
|||||||
//! Represents a block of code which lives inside the Interpreter
|
//! Represents a block of code which lives inside the Interpreter
|
||||||
|
|
||||||
use collect_upvars::collect_upvars;
|
|
||||||
|
|
||||||
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
|
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
|
||||||
use cl_ast::{Function as FnDecl, Param, Sym};
|
use cl_ast::{Function as FnDecl, Param, Sym};
|
||||||
use std::{
|
use std::rc::Rc;
|
||||||
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
|
/// Represents a block of code which persists inside the Interpreter
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
/// Stores the contents of the function declaration
|
/// Stores the contents of the function declaration
|
||||||
decl: Rc<FnDecl>,
|
decl: Rc<FnDecl>,
|
||||||
/// Stores data from the enclosing scopes
|
// /// Stores the enclosing scope of the function
|
||||||
upvars: RefCell<Upvars>,
|
// env: Box<Environment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(decl: &FnDecl) -> Self {
|
pub fn new(decl: &FnDecl) -> Self {
|
||||||
// let upvars = collect_upvars(decl, env);
|
Self { decl: decl.clone().into() }
|
||||||
Self { decl: decl.clone().into(), upvars: Default::default() }
|
|
||||||
}
|
}
|
||||||
pub fn decl(&self) -> &FnDecl {
|
pub fn decl(&self) -> &FnDecl {
|
||||||
&self.decl
|
&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 {
|
impl Callable for Function {
|
||||||
@ -49,7 +28,6 @@ impl Callable for Function {
|
|||||||
}
|
}
|
||||||
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
|
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
|
||||||
|
|
||||||
// Check arg mapping
|
// Check arg mapping
|
||||||
if args.len() != bind.len() {
|
if args.len() != bind.len() {
|
||||||
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
|
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
|
||||||
@ -57,21 +35,12 @@ impl Callable for Function {
|
|||||||
let Some(body) = body else {
|
let Some(body) = body else {
|
||||||
return Err(Error::NotDefined(*name));
|
return Err(Error::NotDefined(*name));
|
||||||
};
|
};
|
||||||
|
|
||||||
let upvars = self.upvars.take();
|
|
||||||
env.push_frame("upvars", upvars);
|
|
||||||
|
|
||||||
// TODO: completely refactor data storage
|
// TODO: completely refactor data storage
|
||||||
let mut frame = env.frame("fn args");
|
let mut frame = env.frame("fn args");
|
||||||
for (Param { mutability: _, name }, value) in bind.iter().zip(args) {
|
for (Param { mutability: _, name }, value) in bind.iter().zip(args) {
|
||||||
frame.insert(*name, Some(value.clone()));
|
frame.insert(*name, Some(value.clone()));
|
||||||
}
|
}
|
||||||
let res = body.interpret(&mut frame);
|
match 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::Return(value)) => Ok(value),
|
||||||
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
|
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
|
||||||
result => result,
|
result => result,
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
//! 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ 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`].
|
||||||
@ -37,10 +36,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(item) => {
|
ItemKind::Use(_) => todo!("namespaces and imports in the interpreter"),
|
||||||
eprintln!("TODO: namespaces and imports in the interpreter!\n{item}\n");
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,21 +67,11 @@ 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;
|
||||||
env.push_frame(Interned::to_ref(name), Default::default());
|
// TODO: Enter this module's namespace
|
||||||
let out = match kind {
|
match kind {
|
||||||
ModuleKind::Inline(file) => file.interpret(env),
|
ModuleKind::Inline(file) => file.interpret(env),
|
||||||
ModuleKind::Outline => {
|
ModuleKind::Outline => Err(Error::Outlined(*name)),
|
||||||
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 {
|
||||||
@ -128,7 +114,14 @@ 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> {
|
||||||
@ -136,14 +129,11 @@ 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::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),
|
||||||
@ -170,306 +160,45 @@ impl Interpret for ExprKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpret for Quote {
|
fn evaluate_place_expr<'e>(
|
||||||
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
// TODO: squoosh down into a ConValue?
|
|
||||||
Ok(ConValue::Quote(self.quote.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpret for Let {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Let { mutable: _, name, ty: _, init } = self;
|
|
||||||
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}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
env: &'e mut Environment,
|
||||||
pat: &ExprKind,
|
expr: &ExprKind,
|
||||||
) -> IResult<&'e mut ConValue> {
|
) -> IResult<(&'e mut Option<ConValue>, Sym)> {
|
||||||
match pat {
|
match expr {
|
||||||
ExprKind::Path(path) => addrof_path(env, &path.parts)?
|
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => {
|
||||||
.as_mut()
|
match parts.last().expect("parts should not be empty") {
|
||||||
.ok_or(Error::NotInitialized("".into())),
|
PathPart::SuperKw => Err(Error::NotAssignable),
|
||||||
ExprKind::Member(member) => addrof_member(env, member),
|
PathPart::SelfKw => todo!("Assignment to `self`"),
|
||||||
ExprKind::Index(index) => addrof_index(env, index),
|
PathPart::SelfTy => todo!("What does it mean to assign to capital-S Self?"),
|
||||||
ExprKind::Group(Group { expr }) => addrof(env, expr),
|
PathPart::Ident(s) => env.get_mut(*s).map(|v| (v, *s)),
|
||||||
ExprKind::AddrOf(AddrOf { mutable: Mutability::Mut, expr }) => addrof(env, expr),
|
|
||||||
_ => Err(Error::TypeError),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExprKind::Index(_) => todo!("Assignment to an index operation"),
|
||||||
pub fn addrof_path<'e>(
|
ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"),
|
||||||
env: &'e mut Environment,
|
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => {
|
||||||
path: &[PathPart],
|
todo!("Pattern Destructuring?")
|
||||||
) -> 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),
|
_ => 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`"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpret for Assign {
|
impl Interpret for Assign {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Assign { parts } = self;
|
let Assign { parts } = self;
|
||||||
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
|
||||||
assignment::assign(env, head, init).map(|_| ConValue::Empty)
|
let target = evaluate_place_expr(env, head)?;
|
||||||
|
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 {
|
||||||
@ -479,7 +208,10 @@ 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 = assignment::addrof(env, head)?;
|
let target = evaluate_place_expr(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),
|
||||||
@ -626,9 +358,6 @@ fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
|||||||
ConValue::Bool(b) => b as _,
|
ConValue::Bool(b) => b as _,
|
||||||
ConValue::Char(c) => c as _,
|
ConValue::Char(c) => c as _,
|
||||||
ConValue::Ref(v) => return cast((*v).clone(), ty),
|
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)?,
|
_ => Err(Error::TypeError)?,
|
||||||
};
|
};
|
||||||
Ok(match &*ty {
|
Ok(match &*ty {
|
||||||
@ -640,8 +369,6 @@ fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
|||||||
"i32" => ConValue::Int(value as i32 as _),
|
"i32" => ConValue::Int(value as i32 as _),
|
||||||
"u64" => ConValue::Int(value),
|
"u64" => ConValue::Int(value),
|
||||||
"i64" => 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}')),
|
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
|
||||||
"bool" => ConValue::Bool(value < 0),
|
"bool" => ConValue::Bool(value < 0),
|
||||||
_ => Err(Error::NotDefined(ty))?,
|
_ => Err(Error::NotDefined(ty))?,
|
||||||
@ -674,20 +401,6 @@ 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 {
|
||||||
@ -711,37 +424,22 @@ 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> {
|
||||||
let Self { to: Path { absolute: _, parts }, init } = self;
|
todo!("struct construction in {env}")
|
||||||
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;
|
||||||
|
|
||||||
assignment::addrof_path(env, parts)
|
if parts.len() == 1 {
|
||||||
.cloned()
|
match parts.last().expect("parts should not be empty") {
|
||||||
.transpose()
|
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"),
|
||||||
.ok_or_else(|| Error::NotInitialized(format!("{self}").into()))?
|
PathPart::SelfTy => todo!("Path navigation to Self"),
|
||||||
|
PathPart::Ident(name) => env.get(*name),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!("Path navigation!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Interpret for Literal {
|
impl Interpret for Literal {
|
||||||
@ -750,7 +448,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) => ConValue::Float(f64::from_bits(*value)),
|
// Literal::Float(value) => todo!("Float values in interpreter: {value:?}"),
|
||||||
Literal::Int(value) => ConValue::Int(*value as _),
|
Literal::Int(value) => ConValue::Int(*value as _),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -778,9 +476,13 @@ 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 { mutable: _, expr } = self;
|
let Self { count: _, 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)?))),
|
||||||
}
|
}
|
||||||
@ -846,19 +548,16 @@ 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: Box<dyn Iterator<Item = ConValue>> = match &cond {
|
let mut bounds = match cond.interpret(env)? {
|
||||||
&ConValue::RangeExc(a, b) => Box::new((a..b).map(ConValue::Int)),
|
ConValue::RangeExc(a, b) => a..=b,
|
||||||
&ConValue::RangeInc(a, b) => Box::new((a..=b).map(ConValue::Int)),
|
ConValue::RangeInc(a, b) => a..=b,
|
||||||
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));
|
env.insert(*name, Some(loop_var.into()));
|
||||||
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,
|
||||||
|
@ -17,6 +17,11 @@ pub trait Callable: std::fmt::Debug {
|
|||||||
fn name(&self) -> Sym;
|
fn name(&self) -> Sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [BuiltIn]s are [Callable]s with bespoke definitions
|
||||||
|
pub trait BuiltIn: std::fmt::Debug + Callable {
|
||||||
|
fn description(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod convalue;
|
pub mod convalue;
|
||||||
|
|
||||||
pub mod interpret;
|
pub mod interpret;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
use crate::{convalue::ConValue, env::Environment, Interpret};
|
use crate::{env::Environment, convalue::ConValue, Interpret};
|
||||||
use cl_ast::*;
|
use cl_ast::*;
|
||||||
use cl_lexer::Lexer;
|
use cl_lexer::Lexer;
|
||||||
use cl_parser::Parser;
|
use cl_parser::Parser;
|
||||||
@ -178,45 +178,6 @@ 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 {
|
||||||
@ -227,7 +188,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 () {}",
|
"fn empty_fn () {\n \n}",
|
||||||
format!(
|
format!(
|
||||||
"{}",
|
"{}",
|
||||||
env.get("empty_fn".into())
|
env.get("empty_fn".into())
|
||||||
@ -476,17 +437,16 @@ mod operators {
|
|||||||
env_eq!(env.y, 10);
|
env_eq!(env.y, 10);
|
||||||
env_eq!(env.z, 10);
|
env_eq!(env.z, 10);
|
||||||
}
|
}
|
||||||
// Test is disabled, since new assignment system intentionally does not care.
|
#[test]
|
||||||
// #[test]
|
#[should_panic]
|
||||||
// #[should_panic]
|
fn assignment_accounts_for_type() {
|
||||||
// fn assignment_accounts_for_type() {
|
let mut env = Default::default();
|
||||||
// let mut env = Default::default();
|
assert_eval!(env,
|
||||||
// assert_eval!(env,
|
let x = "a string";
|
||||||
// let x = "a string";
|
let y = 0xdeadbeef;
|
||||||
// let y = 0xdeadbeef;
|
y = x; // should crash: type error
|
||||||
// y = x; // should crash: type error
|
);
|
||||||
// );
|
}
|
||||||
// }
|
|
||||||
#[test]
|
#[test]
|
||||||
fn precedence() {
|
fn precedence() {
|
||||||
let mut env = Default::default();
|
let mut env = Default::default();
|
||||||
@ -509,56 +469,6 @@ 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();
|
||||||
|
@ -23,7 +23,7 @@ pub mod lexer_iter {
|
|||||||
pub struct LexerIter<'t> {
|
pub struct LexerIter<'t> {
|
||||||
lexer: Lexer<'t>,
|
lexer: Lexer<'t>,
|
||||||
}
|
}
|
||||||
impl Iterator for LexerIter<'_> {
|
impl<'t> Iterator for LexerIter<'t> {
|
||||||
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() {
|
||||||
@ -192,7 +192,7 @@ impl<'t> Lexer<'t> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Digraphs and trigraphs
|
/// Digraphs and trigraphs
|
||||||
impl Lexer<'_> {
|
impl<'t> Lexer<'t> {
|
||||||
fn amp(&mut self) -> LResult<Token> {
|
fn amp(&mut self) -> LResult<Token> {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Ok('&') => self.consume()?.produce_op(Kind::AmpAmp),
|
Ok('&') => self.consume()?.produce_op(Kind::AmpAmp),
|
||||||
@ -319,7 +319,7 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Comments
|
/// Comments
|
||||||
impl Lexer<'_> {
|
impl<'t> Lexer<'t> {
|
||||||
fn line_comment(&mut self) -> LResult<Token> {
|
fn line_comment(&mut self) -> LResult<Token> {
|
||||||
let mut comment = String::new();
|
let mut comment = String::new();
|
||||||
while Ok('\n') != self.peek() {
|
while Ok('\n') != self.peek() {
|
||||||
@ -339,7 +339,7 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Identifiers
|
/// Identifiers
|
||||||
impl Lexer<'_> {
|
impl<'t> Lexer<'t> {
|
||||||
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() {
|
||||||
@ -371,39 +371,23 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Integers
|
/// Integers
|
||||||
impl Lexer<'_> {
|
impl<'t> Lexer<'t> {
|
||||||
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 = 0;
|
let mut value = self.digit::<B>()? as u128;
|
||||||
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;
|
||||||
}
|
}
|
||||||
// TODO: find a better way to handle floats in the tokenizer
|
self.produce(Kind::Literal, value)
|
||||||
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()?;
|
||||||
@ -414,7 +398,7 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Strings and characters
|
/// Strings and characters
|
||||||
impl Lexer<'_> {
|
impl<'t> Lexer<'t> {
|
||||||
fn string(&mut self) -> LResult<Token> {
|
fn string(&mut self) -> LResult<Token> {
|
||||||
let mut value = String::new();
|
let mut value = String::new();
|
||||||
while '"'
|
while '"'
|
||||||
|
@ -119,10 +119,6 @@ pub enum Parsing {
|
|||||||
Break,
|
Break,
|
||||||
Return,
|
Return,
|
||||||
Continue,
|
Continue,
|
||||||
|
|
||||||
Pattern,
|
|
||||||
Match,
|
|
||||||
MatchArm,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
@ -229,10 +225,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ impl Parse<'_> for Literal {
|
|||||||
TokenData::String(v) => Literal::String(v),
|
TokenData::String(v) => Literal::String(v),
|
||||||
TokenData::Character(v) => Literal::Char(v),
|
TokenData::Character(v) => Literal::Char(v),
|
||||||
TokenData::Integer(v) => Literal::Int(v),
|
TokenData::Integer(v) => Literal::Int(v),
|
||||||
TokenData::Float(v) => Literal::Float(v.to_bits()),
|
TokenData::Float(v) => todo!("Literal::Float({v})"),
|
||||||
_ => panic!("Expected token data for {ty:?}"),
|
_ => panic!("Expected token data for {ty:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -900,24 +900,12 @@ impl Parse<'_> for ExprKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse<'_> for Quote {
|
|
||||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
|
||||||
let quote = delim(
|
|
||||||
ExprKind::parse,
|
|
||||||
(TokenKind::Grave, TokenKind::Grave),
|
|
||||||
Parsing::ExprKind,
|
|
||||||
)(p)?
|
|
||||||
.into();
|
|
||||||
Ok(Quote { quote })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse<'_> for Let {
|
impl Parse<'_> for Let {
|
||||||
fn parse(p: &mut Parser) -> PResult<Let> {
|
fn parse(p: &mut Parser) -> PResult<Let> {
|
||||||
p.consume_peeked();
|
p.consume_peeked();
|
||||||
Ok(Let {
|
Ok(Let {
|
||||||
mutable: Mutability::parse(p)?,
|
mutable: Mutability::parse(p)?,
|
||||||
name: Pattern::parse(p)?,
|
name: Sym::parse(p)?,
|
||||||
ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() {
|
ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() {
|
||||||
Some(Ty::parse(p)?.into())
|
Some(Ty::parse(p)?.into())
|
||||||
} else {
|
} else {
|
||||||
@ -974,24 +962,16 @@ impl Parse<'_> for AddrOf {
|
|||||||
/// [AddrOf] = (`&`|`&&`)* [Expr]
|
/// [AddrOf] = (`&`|`&&`)* [Expr]
|
||||||
fn parse(p: &mut Parser) -> PResult<AddrOf> {
|
fn parse(p: &mut Parser) -> PResult<AddrOf> {
|
||||||
const P: Parsing = Parsing::AddrOf;
|
const P: Parsing = Parsing::AddrOf;
|
||||||
match p.peek_kind(P)? {
|
let mut count = 0;
|
||||||
TokenKind::Amp => {
|
loop {
|
||||||
|
count += match p.peek_kind(P)? {
|
||||||
|
TokenKind::Amp => 1,
|
||||||
|
TokenKind::AmpAmp => 2,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
p.consume_peeked();
|
p.consume_peeked();
|
||||||
Ok(AddrOf { mutable: Mutability::parse(p)?, expr: ExprKind::parse(p)?.into() })
|
|
||||||
}
|
|
||||||
TokenKind::AmpAmp => {
|
|
||||||
p.consume_peeked();
|
|
||||||
Ok(AddrOf {
|
|
||||||
mutable: Mutability::Not,
|
|
||||||
expr: ExprKind::AddrOf(AddrOf {
|
|
||||||
mutable: Mutability::parse(p)?,
|
|
||||||
expr: ExprKind::parse(p)?.into(),
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
got => Err(p.error(ExpectedToken { want: TokenKind::Amp, got }, P)),
|
|
||||||
}
|
}
|
||||||
|
Ok(AddrOf { count, mutable: Mutability::parse(p)?, expr: ExprKind::parse(p)?.into() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1075,38 +1055,6 @@ impl Parse<'_> for Return {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse<'_> for Pattern {
|
|
||||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
|
||||||
let value = prec::exprkind(p, prec::Precedence::Highest.level())?;
|
|
||||||
Pattern::try_from(value)
|
|
||||||
.map_err(|_| p.error(ExpectedParsing { want: Parsing::Pattern }, Parsing::Pattern))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse<'_> for Match {
|
|
||||||
/// [Match] = `match` [Expr] `{` [MatchArm],* `}`
|
|
||||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
|
||||||
p.match_type(TokenKind::Match, Parsing::Match)?;
|
|
||||||
let scrutinee = Expr::parse(p)?.into();
|
|
||||||
let arms = delim(
|
|
||||||
sep(MatchArm::parse, TokenKind::Comma, CURLIES.1, Parsing::Match),
|
|
||||||
CURLIES,
|
|
||||||
Parsing::Match,
|
|
||||||
)(p)?;
|
|
||||||
Ok(Match { scrutinee, arms })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse<'_> for MatchArm {
|
|
||||||
/// [MatchArm] = [Pattern] `=>` [Expr]
|
|
||||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
|
||||||
let pat = Pattern::parse(p)?;
|
|
||||||
p.match_type(TokenKind::FatArrow, Parsing::MatchArm)?;
|
|
||||||
let expr = Expr::parse(p)?;
|
|
||||||
Ok(MatchArm(pat, expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ret_body = (*unconsumed* `;` | [Expr])
|
/// ret_body = (*unconsumed* `;` | [Expr])
|
||||||
fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> {
|
fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> {
|
||||||
Ok(match p.peek_kind(while_parsing)? {
|
Ok(match p.peek_kind(while_parsing)? {
|
||||||
|
@ -16,12 +16,10 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
|||||||
literal_like!() => Literal::parse(p)?.into(),
|
literal_like!() => Literal::parse(p)?.into(),
|
||||||
path_like!() => exprkind_pathlike(p)?,
|
path_like!() => exprkind_pathlike(p)?,
|
||||||
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
|
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
|
||||||
TokenKind::Grave => Quote::parse(p)?.into(),
|
|
||||||
TokenKind::LCurly => Block::parse(p)?.into(),
|
TokenKind::LCurly => Block::parse(p)?.into(),
|
||||||
TokenKind::LBrack => exprkind_arraylike(p)?,
|
TokenKind::LBrack => exprkind_arraylike(p)?,
|
||||||
TokenKind::LParen => exprkind_tuplelike(p)?,
|
TokenKind::LParen => exprkind_tuplelike(p)?,
|
||||||
TokenKind::Let => Let::parse(p)?.into(),
|
TokenKind::Let => Let::parse(p)?.into(),
|
||||||
TokenKind::Match => Match::parse(p)?.into(),
|
|
||||||
TokenKind::While => ExprKind::While(While::parse(p)?),
|
TokenKind::While => ExprKind::While(While::parse(p)?),
|
||||||
TokenKind::If => ExprKind::If(If::parse(p)?),
|
TokenKind::If => ExprKind::If(If::parse(p)?),
|
||||||
TokenKind::For => ExprKind::For(For::parse(p)?),
|
TokenKind::For => ExprKind::For(For::parse(p)?),
|
||||||
@ -33,7 +31,8 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
op => {
|
op => {
|
||||||
let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
|
let (kind, prec) =
|
||||||
|
from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
|
||||||
let ((), after) = prec.prefix().expect("should have a precedence");
|
let ((), after) = prec.prefix().expect("should have a precedence");
|
||||||
p.consume_peeked();
|
p.consume_peeked();
|
||||||
Unary { kind, tail: exprkind(p, after)?.into() }.into()
|
Unary { kind, tail: exprkind(p, after)?.into() }.into()
|
||||||
@ -65,9 +64,13 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
|||||||
ExprKind::Index(Index { head: head.into(), indices })
|
ExprKind::Index(Index { head: head.into(), indices })
|
||||||
}
|
}
|
||||||
TokenKind::LParen => {
|
TokenKind::LParen => {
|
||||||
let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
|
let exprs =
|
||||||
|
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
|
||||||
p.match_type(TokenKind::RParen, parsing)?;
|
p.match_type(TokenKind::RParen, parsing)?;
|
||||||
Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() }
|
Binary {
|
||||||
|
kind: BinaryKind::Call,
|
||||||
|
parts: (head, Tuple { exprs }.into()).into(),
|
||||||
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
TokenKind::Dot => {
|
TokenKind::Dot => {
|
||||||
@ -244,9 +247,9 @@ fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Assign,
|
Assign,
|
||||||
Logic,
|
|
||||||
Compare,
|
Compare,
|
||||||
Range,
|
Range,
|
||||||
|
Logic,
|
||||||
Bitwise,
|
Bitwise,
|
||||||
Shift,
|
Shift,
|
||||||
Factor,
|
Factor,
|
||||||
@ -256,7 +259,6 @@ pub enum Precedence {
|
|||||||
Cast,
|
Cast,
|
||||||
Member, // left-associative
|
Member, // left-associative
|
||||||
Call,
|
Call,
|
||||||
Highest,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Precedence {
|
impl Precedence {
|
||||||
@ -307,7 +309,9 @@ impl From<BinaryKind> for Precedence {
|
|||||||
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
|
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
|
||||||
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
|
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
|
||||||
Op::RangeExc | Op::RangeInc => Precedence::Range,
|
Op::RangeExc | Op::RangeInc => Precedence::Range,
|
||||||
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
|
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => {
|
||||||
|
Precedence::Compare
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,19 +120,19 @@ pub mod yamler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Section<'_> {
|
impl<'y> Deref for Section<'y> {
|
||||||
type Target = Yamler;
|
type Target = Yamler;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.yamler
|
self.yamler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DerefMut for Section<'_> {
|
impl<'y> DerefMut for Section<'y> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.yamler
|
self.yamler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Section<'_> {
|
impl<'y> Drop for Section<'y> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self { yamler } = self;
|
let Self { yamler } = self;
|
||||||
yamler.decrease();
|
yamler.decrease();
|
||||||
@ -369,6 +369,16 @@ pub mod yamlify {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
@ -378,9 +388,7 @@ 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::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),
|
||||||
@ -409,60 +417,6 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
||||||
@ -571,8 +525,11 @@ 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 { mutable, expr } = self;
|
let Self { count, mutable, expr } = self;
|
||||||
y.key("AddrOf").yaml(mutable).pair("expr", expr);
|
y.key("AddrOf")
|
||||||
|
.pair("count", count)
|
||||||
|
.yaml(mutable)
|
||||||
|
.pair("expr", expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Group {
|
impl Yamlify for Group {
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
tools::print_token,
|
tools::print_token,
|
||||||
};
|
};
|
||||||
use cl_ast::File;
|
use cl_ast::File;
|
||||||
use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
|
use cl_interpret::{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};
|
||||||
@ -16,31 +16,6 @@ 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)?;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use cl_interpret::{convalue::ConValue, env::Environment, error::IResult, interpret::Interpret};
|
use cl_interpret::{
|
||||||
|
env::Environment, error::IResult, interpret::Interpret, convalue::ConValue,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use crate::{ansi, ctx};
|
use crate::{ansi, ctx};
|
||||||
use cl_ast::Stmt;
|
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::*};
|
||||||
|
|
||||||
pub fn clear() {
|
fn clear() {
|
||||||
println!("{}", ansi::CLEAR_ALL);
|
println!("{}", ansi::CLEAR_ALL);
|
||||||
banner()
|
banner()
|
||||||
}
|
}
|
||||||
@ -49,7 +48,6 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
|
|||||||
|
|
||||||
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),
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,4 @@ license.workspace = true
|
|||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cl-arena = { version = "0", registry = "soft-fish" }
|
cl-arena = { path = "../cl-arena" }
|
||||||
|
@ -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> {
|
) -> Result<[&mut V; N], GetManyMutError<N>> {
|
||||||
self.map.get_many_mut(indices.map(|id| id.get()))
|
self.map.get_many_mut(indices.map(|id| id.get()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,14 +35,9 @@ 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<T: ?Sized + Debug> Debug for Interned<'_, T> {
|
impl<'a, T: ?Sized + Debug> Debug for Interned<'a, 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)
|
||||||
@ -54,14 +49,14 @@ pub mod interned {
|
|||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ?Sized> Deref for Interned<'_, T> {
|
impl<'a, T: ?Sized> Deref for Interned<'a, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ?Sized> Copy for Interned<'_, T> {}
|
impl<'a, T: ?Sized> Copy for Interned<'a, T> {}
|
||||||
impl<T: ?Sized> Clone for Interned<'_, T> {
|
impl<'a, T: ?Sized> Clone for Interned<'a, T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
@ -84,13 +79,13 @@ pub mod interned {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl<T: ?Sized> Eq for Interned<'_, T> {}
|
impl<'a, T: ?Sized> Eq for Interned<'a, T> {}
|
||||||
impl<T: ?Sized> PartialEq for Interned<'_, T> {
|
impl<'a, T: ?Sized> PartialEq for Interned<'a, 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<T: ?Sized> Hash for Interned<'_, T> {
|
impl<'a, T: ?Sized> Hash for Interned<'a, 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)
|
||||||
}
|
}
|
||||||
@ -124,7 +119,6 @@ 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>>,
|
||||||
@ -213,8 +207,8 @@ pub mod string_interner {
|
|||||||
// 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 Send for StringInterner<'_> {}
|
unsafe impl<'a> Send for StringInterner<'a> {}
|
||||||
unsafe impl Sync for StringInterner<'_> {}
|
unsafe impl<'a> Sync for StringInterner<'a> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -264,7 +258,6 @@ 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>>,
|
||||||
@ -311,5 +304,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<T: Eq + Hash + Send + Sync> Sync for TypedInterner<'_, T> {}
|
unsafe impl<'a, T: Eq + Hash + Send + Sync> Sync for TypedInterner<'a, T> {}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ pub enum TokenKind {
|
|||||||
In, // "in"
|
In, // "in"
|
||||||
Let, // "let"
|
Let, // "let"
|
||||||
Loop, // "loop"
|
Loop, // "loop"
|
||||||
Match, // "match"
|
|
||||||
Mod, // "mod"
|
Mod, // "mod"
|
||||||
Mut, // "mut"
|
Mut, // "mut"
|
||||||
Pub, // "pub"
|
Pub, // "pub"
|
||||||
@ -122,7 +121,6 @@ impl Display for TokenKind {
|
|||||||
TokenKind::In => "in".fmt(f),
|
TokenKind::In => "in".fmt(f),
|
||||||
TokenKind::Let => "let".fmt(f),
|
TokenKind::Let => "let".fmt(f),
|
||||||
TokenKind::Loop => "loop".fmt(f),
|
TokenKind::Loop => "loop".fmt(f),
|
||||||
TokenKind::Match => "match".fmt(f),
|
|
||||||
TokenKind::Mod => "mod".fmt(f),
|
TokenKind::Mod => "mod".fmt(f),
|
||||||
TokenKind::Mut => "mut".fmt(f),
|
TokenKind::Mut => "mut".fmt(f),
|
||||||
TokenKind::Pub => "pub".fmt(f),
|
TokenKind::Pub => "pub".fmt(f),
|
||||||
@ -215,7 +213,6 @@ impl FromStr for TokenKind {
|
|||||||
"in" => Self::In,
|
"in" => Self::In,
|
||||||
"let" => Self::Let,
|
"let" => Self::Let,
|
||||||
"loop" => Self::Loop,
|
"loop" => Self::Loop,
|
||||||
"match" => Self::Match,
|
|
||||||
"mod" => Self::Mod,
|
"mod" => Self::Mod,
|
||||||
"mut" => Self::Mut,
|
"mut" => Self::Mut,
|
||||||
"pub" => Self::Pub,
|
"pub" => Self::Pub,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use cl_structures::intern::string_interner::StringInterner;
|
||||||
use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression};
|
use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression};
|
||||||
|
|
||||||
use cl_ast::{
|
use cl_ast::{
|
||||||
@ -7,12 +8,10 @@ use cl_ast::{
|
|||||||
};
|
};
|
||||||
use cl_lexer::Lexer;
|
use cl_lexer::Lexer;
|
||||||
use cl_parser::{inliner::ModuleInliner, Parser};
|
use cl_parser::{inliner::ModuleInliner, Parser};
|
||||||
use cl_structures::intern::string_interner::StringInterner;
|
|
||||||
use repline::{error::Error as RlError, prebaked::*};
|
use repline::{error::Error as RlError, prebaked::*};
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
path::{self, PathBuf},
|
path::{self, PathBuf},
|
||||||
sync::LazyLock,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Path to display in standard library errors
|
// Path to display in standard library errors
|
||||||
@ -31,6 +30,11 @@ const C_BYID: &str = "\x1b[95m";
|
|||||||
const C_ERROR: &str = "\x1b[31m";
|
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 = Table::default();
|
let mut prj = Table::default();
|
||||||
|
|
||||||
@ -44,7 +48,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
};
|
};
|
||||||
// This code is special - it gets loaded from a hard-coded project directory (for now)
|
// This code is special - it gets loaded from a hard-coded project directory (for now)
|
||||||
let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
|
let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
|
||||||
Populator::new(&mut prj).visit_file(interned(code));
|
Populator::new(&mut prj).visit_file(unsafe { TREES.push(code) });
|
||||||
|
|
||||||
main_menu(&mut prj)?;
|
main_menu(&mut prj)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -94,8 +98,8 @@ fn enter_code(prj: &mut Table) -> Result<(), RlError> {
|
|||||||
let code = Parser::new(Lexer::new(line)).parse()?;
|
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
|
||||||
Populator::new(prj).visit_file(interned(code));
|
Populator::new(prj).visit_file(unsafe { TREES.push(code) });
|
||||||
Ok(Response::Accept)
|
Ok(Response::Accept)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -218,7 +222,7 @@ fn import_files(table: &mut Table) -> Result<(), RlError> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Populator::new(table).visit_file(interned(code));
|
Populator::new(table).visit_file(unsafe { TREES.push(code) });
|
||||||
|
|
||||||
println!("...Imported!");
|
println!("...Imported!");
|
||||||
Ok(Response::Accept)
|
Ok(Response::Accept)
|
||||||
@ -318,11 +322,18 @@ fn banner() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns a [File](cl_ast::File), returning a static reference to it.
|
/// Keeps leaked references to past ASTs, for posterity:tm:
|
||||||
fn interned(file: cl_ast::File) -> &'static cl_ast::File {
|
struct TreeManager {
|
||||||
use cl_structures::intern::{interned::Interned, typed_interner::TypedInterner};
|
trees: Vec<&'static cl_ast::File>,
|
||||||
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ pub enum Source<'a> {
|
|||||||
Ty(&'a TyKind),
|
Ty(&'a TyKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source<'_> {
|
impl<'a> Source<'a> {
|
||||||
pub fn name(&self) -> Option<Sym> {
|
pub fn name(&self) -> Option<Sym> {
|
||||||
match self {
|
match self {
|
||||||
Source::Root => None,
|
Source::Root => None,
|
||||||
@ -32,7 +32,7 @@ impl Source<'_> {
|
|||||||
Source::Const(v) => Some(v.name),
|
Source::Const(v) => Some(v.name),
|
||||||
Source::Static(v) => Some(v.name),
|
Source::Static(v) => Some(v.name),
|
||||||
Source::Function(v) => Some(v.name),
|
Source::Function(v) => Some(v.name),
|
||||||
Source::Local(_) => None,
|
Source::Local(l) => Some(l.name),
|
||||||
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
|
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,11 +146,11 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
||||||
let cl_ast::Let { mutable, name: _, ty, init } = l;
|
let cl_ast::Let { mutable, name, ty, init } = l;
|
||||||
let mut entry = self.new_entry(NodeKind::Local);
|
let mut entry = self.new_entry(NodeKind::Local);
|
||||||
|
|
||||||
entry.inner.set_source(Source::Local(l));
|
entry.inner.set_source(Source::Local(l));
|
||||||
// entry.set_name(*name);
|
entry.set_name(*name);
|
||||||
|
|
||||||
entry.visit_mutability(mutable);
|
entry.visit_mutability(mutable);
|
||||||
if let Some(ty) = ty {
|
if let Some(ty) = ty {
|
||||||
@ -160,8 +160,7 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
|
|||||||
entry.visit_expr(init)
|
entry.visit_expr(init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// let child = entry.inner.id();
|
let child = entry.inner.id();
|
||||||
// self.inner.add_child(*name, child);
|
self.inner.add_child(*name, child);
|
||||||
todo!("Pattern destructuring in cl-typeck")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ impl<'a> Table<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Table<'_> {
|
impl<'a> Default for Table<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
//! 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(())
|
|
||||||
}
|
|
@ -302,7 +302,7 @@ impl<'a> Editor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e> IntoIterator for &'e Editor<'_> {
|
impl<'a, 'e> IntoIterator for &'e Editor<'a> {
|
||||||
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<'e> IntoIterator for &'e Editor<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Editor<'_> {
|
impl<'a> Display for Editor<'a> {
|
||||||
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() {
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
#!/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)
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
#!/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))
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user