Compare commits

..

No commits in common. "main" and "new_ast" have entirely different histories.

41 changed files with 927 additions and 1614 deletions

View File

@ -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"

View 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]

View 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);
}

View 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;
}

View 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);
}
}

View File

@ -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>,
} }

View File

@ -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 }
}
}
} }

View File

@ -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)),

View File

@ -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),

View File

@ -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)
} }

View File

@ -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)]

View File

@ -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![
/// Unstable variadic format function /// Unstable variadic format function
fn fmt(args @ ..) { pub fn format<_, args> () -> IResult<ConValue> {
use std::fmt::Write; use std::fmt::Write;
let mut out = String::new(); let mut out = String::new();
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) { for arg in args {
eprintln!("{e}"); write!(out, "{arg}").ok();
} }
Ok(out) Ok(ConValue::String(out.into()))
} }
/// Prints the arguments in-order, with no separators /// Unstable variadic print function
fn print(args @ ..) { pub fn print<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock(); let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok(); for arg in args {
Ok(()) write!(out, "{arg}").ok();
}
Ok(ConValue::Empty)
} }
/// Prints the arguments in-order, followed by a newline /// Unstable variadic println function
fn println(args @ ..) { pub fn println<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock(); let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok(); for arg in args {
write!(out, "{arg}").ok();
}
writeln!(out).ok(); writeln!(out).ok();
Ok(()) Ok(ConValue::Empty)
} }
/// Debug-prints the argument, returning a copy /// Prints the [Debug](std::fmt::Debug) 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)
}
}

View File

@ -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())
} }
} }

View File

@ -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();
} }

View File

@ -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}"),
} }
} }
} }

View File

@ -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,

View File

@ -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);
});
});
}
}
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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();

View File

@ -23,7 +23,7 @@ pub mod lexer_iter {
pub struct LexerIter<'t> { pub struct LexerIter<'t> {
lexer: Lexer<'t>, lexer: Lexer<'t>,
} }
impl 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 '"'

View File

@ -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)
} }

View File

@ -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)? {

View File

@ -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
}
} }
} }
} }

View File

@ -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 {

View File

@ -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)?;
} }

View File

@ -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 {

View File

@ -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),
} }

View File

@ -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" }

View File

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

View File

@ -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> {}
} }

View File

@ -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,

View File

@ -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
}
} }

View File

@ -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,
} }
} }

View File

@ -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")
} }
} }

View File

@ -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()
} }

View File

@ -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(())
}

View File

@ -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() {

View File

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

View File

@ -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)
}

View File

@ -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))
}