45 Commits

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

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

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

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

cl_parser: Smuggle floats

cl_interpret: unpack smuggled floats in float literal node
2024-09-19 13:20:19 -05:00
56e71d6782 cl-lexer: Add a hacky workaround for float support.
It's disgusting, but better than nothing!
2024-09-19 13:16:27 -05:00
c62df3d8b3 sample-code: Add square root demo 2024-09-18 01:53:04 -05:00
fad28beb05 interpreter: Add float machinery
- operators
- type casting
2024-09-18 01:02:09 -05:00
0f8b0824ac cl-parser: Fix precedence of comparison operators 2024-09-18 00:57:44 -05:00
99a00875a8 cl-intern: Derive Default for StringInterner and TypedInterner 2024-08-24 18:18:22 -05:00
45 changed files with 1894 additions and 1028 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
//! - [Assign], [Modify], [Binary], and [Unary] expressions //! - [Assign], [Modify], [Binary], and [Unary] expressions
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators //! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers //! - [Ty] and [TyKind]: Type qualifiers
//! - [Pattern]: Pattern matching operators
//! - [Path]: Path expressions //! - [Path]: Path expressions
use cl_structures::{intern::interned::Interned, span::*}; use cl_structures::{intern::interned::Interned, span::*};
@@ -36,6 +37,7 @@ pub enum Literal {
Bool(bool), Bool(bool),
Char(char), Char(char),
Int(u128), Int(u128),
Float(u64),
String(String), String(String),
} }
@@ -79,7 +81,6 @@ pub struct Item {
/// What kind of [Item] is this? /// What kind of [Item] is this?
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemKind { pub enum ItemKind {
// TODO: Import declaration ("use") item
// TODO: Trait declaration ("trait") item? // TODO: Trait declaration ("trait") item?
/// A [module](Module) /// A [module](Module)
Module(Module), Module(Module),
@@ -346,8 +347,12 @@ pub enum ExprKind {
/// An empty expression: `(` `)` /// An empty expression: `(` `)`
#[default] #[default]
Empty, Empty,
/// A backtick-quoted expression
Quote(Quote),
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`] /// 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`])\+
@@ -395,15 +400,43 @@ 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: Sym, pub name: Pattern,
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 {
@@ -486,6 +519,7 @@ 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>,
@@ -545,7 +579,6 @@ pub struct ArrayRep {
/// An address-of expression: `&` `mut`? [`Expr`] /// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf { pub struct AddrOf {
pub count: usize,
pub mutable: Mutability, pub mutable: Mutability,
pub expr: Box<ExprKind>, pub expr: Box<ExprKind>,
} }

View File

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

View File

@@ -37,6 +37,9 @@ pub trait Fold {
fn fold_int(&mut self, i: u128) -> u128 { fn fold_int(&mut self, i: u128) -> u128 {
i i
} }
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
f
}
fn fold_string(&mut self, s: String) -> String { fn fold_string(&mut self, s: String) -> String {
s s
} }
@@ -226,15 +229,6 @@ pub trait Fold {
fn fold_semi(&mut self, s: Semi) -> Semi { fn fold_semi(&mut self, s: Semi) -> Semi {
s s
} }
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_sym(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_expr(&mut self, e: Expr) -> Expr { fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e; let Expr { extents, kind } = e;
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
@@ -242,6 +236,56 @@ pub trait Fold {
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind) or_fold_expr_kind(self, kind)
} }
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_pattern(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
match p {
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
Pattern::Ref(mutability, pattern) => Pattern::Ref(
self.fold_mutability(mutability),
Box::new(self.fold_pattern(*pattern)),
),
Pattern::Tuple(patterns) => {
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Array(patterns) => {
Pattern::Array(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Struct(path, items) => Pattern::Struct(
self.fold_path(path),
items
.into_iter()
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
.collect(),
),
}
}
fn fold_match(&mut self, m: Match) -> Match {
let Match { scrutinee, arms } = m;
Match {
scrutinee: self.fold_expr(*scrutinee).into(),
arms: arms
.into_iter()
.map(|arm| self.fold_match_arm(arm))
.collect(),
}
}
fn fold_match_arm(&mut self, a: MatchArm) -> MatchArm {
let MatchArm(pat, expr) = a;
MatchArm(self.fold_pattern(pat), self.fold_expr(expr))
}
fn fold_assign(&mut self, a: Assign) -> Assign { fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = *parts; let (head, tail) = *parts;
@@ -319,9 +363,8 @@ pub trait Fold {
} }
} }
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf { fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
let AddrOf { count, mutable, expr } = a; let AddrOf { mutable, expr } = a;
AddrOf { AddrOf {
count,
mutable: self.fold_mutability(mutable), mutable: self.fold_mutability(mutable),
expr: Box::new(self.fold_expr_kind(*expr)), expr: Box::new(self.fold_expr_kind(*expr)),
} }
@@ -384,6 +427,7 @@ pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Litera
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)), Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
Literal::Char(c) => Literal::Char(folder.fold_char(c)), Literal::Char(c) => Literal::Char(folder.fold_char(c)),
Literal::Int(i) => Literal::Int(folder.fold_int(i)), Literal::Int(i) => Literal::Int(folder.fold_int(i)),
Literal::Float(f) => Literal::Float(folder.fold_smuggled_float(f)),
Literal::String(s) => Literal::String(folder.fold_string(s)), Literal::String(s) => Literal::String(folder.fold_string(s)),
} }
} }
@@ -526,7 +570,9 @@ 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,6 +23,7 @@ pub trait Visit<'a>: Sized {
fn visit_bool(&mut self, _b: &'a bool) {} fn visit_bool(&mut self, _b: &'a bool) {}
fn visit_char(&mut self, _c: &'a char) {} fn visit_char(&mut self, _c: &'a char) {}
fn visit_int(&mut self, _i: &'a u128) {} fn visit_int(&mut self, _i: &'a u128) {}
fn visit_smuggled_float(&mut self, _f: &'a u64) {}
fn visit_string(&mut self, _s: &'a str) {} fn visit_string(&mut self, _s: &'a str) {}
fn visit_file(&mut self, f: &'a File) { fn visit_file(&mut self, f: &'a File) {
let File { items } = f; let File { items } = f;
@@ -191,17 +192,6 @@ pub trait Visit<'a>: Sized {
or_visit_stmt_kind(self, kind) or_visit_stmt_kind(self, kind)
} }
fn visit_semi(&mut self, _s: &'a Semi) {} fn visit_semi(&mut self, _s: &'a Semi) {}
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_sym(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_expr(&mut self, e: &'a Expr) { fn visit_expr(&mut self, e: &'a Expr) {
let Expr { extents, kind } = e; let Expr { extents, kind } = e;
self.visit_span(extents); self.visit_span(extents);
@@ -210,6 +200,55 @@ pub trait Visit<'a>: Sized {
fn visit_expr_kind(&mut self, e: &'a ExprKind) { fn visit_expr_kind(&mut self, e: &'a ExprKind) {
or_visit_expr_kind(self, e) or_visit_expr_kind(self, e)
} }
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_pattern(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_pattern(&mut self, p: &'a Pattern) {
match p {
Pattern::Path(path) => self.visit_path(path),
Pattern::Literal(literal) => self.visit_literal(literal),
Pattern::Ref(mutability, pattern) => {
self.visit_mutability(mutability);
self.visit_pattern(pattern);
}
Pattern::Tuple(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Array(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Struct(path, items) => {
self.visit_path(path);
items.iter().for_each(|(_name, bind)| {
bind.as_ref().inspect(|bind| {
self.visit_pattern(bind);
});
});
}
}
}
fn visit_match(&mut self, m: &'a Match) {
let Match { scrutinee, arms } = m;
self.visit_expr(scrutinee);
arms.iter().for_each(|arm| self.visit_match_arm(arm));
}
fn visit_match_arm(&mut self, a: &'a MatchArm) {
let MatchArm(pat, expr) = a;
self.visit_pattern(pat);
self.visit_expr(expr);
}
fn visit_assign(&mut self, a: &'a Assign) { fn visit_assign(&mut self, a: &'a Assign) {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = parts.as_ref(); let (head, tail) = parts.as_ref();
@@ -278,7 +317,7 @@ pub trait Visit<'a>: Sized {
self.visit_expr_kind(repeat); self.visit_expr_kind(repeat);
} }
fn visit_addrof(&mut self, a: &'a AddrOf) { fn visit_addrof(&mut self, a: &'a AddrOf) {
let AddrOf { count: _, mutable, expr } = a; let AddrOf { mutable, expr } = a;
self.visit_mutability(mutable); self.visit_mutability(mutable);
self.visit_expr_kind(expr); self.visit_expr_kind(expr);
} }
@@ -339,6 +378,7 @@ pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
Literal::Bool(b) => visitor.visit_bool(b), Literal::Bool(b) => visitor.visit_bool(b),
Literal::Char(c) => visitor.visit_char(c), Literal::Char(c) => visitor.visit_char(c),
Literal::Int(i) => visitor.visit_int(i), Literal::Int(i) => visitor.visit_int(i),
Literal::Float(f) => visitor.visit_smuggled_float(f),
Literal::String(s) => visitor.visit_string(s), Literal::String(s) => visitor.visit_string(s),
} }
} }
@@ -450,7 +490,9 @@ 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, F: Write + ?Sized> Write for Indent<'f, F> { impl<F: Write + ?Sized> Write for Indent<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result { fn write_str(&mut self, s: &str) -> std::fmt::Result {
for s in s.split_inclusive('\n') { for s in s.split_inclusive('\n') {
self.f.write_str(s)?; self.f.write_str(s)?;
@@ -45,14 +45,14 @@ impl<'f, F: Write + ?Sized> Delimit<'f, F> {
Self { f, delim } Self { f, delim }
} }
} }
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, F> { impl<F: Write + ?Sized> Drop for Delimit<'_, F> {
fn drop(&mut self) { fn drop(&mut self) {
let Self { f: Indent { f, .. }, delim } = self; let Self { f: Indent { f, .. }, delim } = self;
let _ = f.write_str(delim.close); let _ = f.write_str(delim.close);
} }
} }
impl<'f, F: Write + ?Sized> Write for Delimit<'f, F> { impl<F: Write + ?Sized> Write for Delimit<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result { fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.f.write_str(s) self.f.write_str(s)
} }

View File

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

View File

@@ -1,81 +1,192 @@
//! Implementations of built-in functions #![allow(non_upper_case_globals)]
use super::{ use crate::{
convalue::ConValue, convalue::ConValue,
env::Environment, env::Environment,
error::{Error, IResult}, error::{Error, IResult},
BuiltIn, Callable,
}; };
use cl_ast::Sym;
use std::{ use std::{
io::{stdout, Write}, io::{stdout, Write},
rc::Rc,
slice, slice,
}; };
builtins! { /// A function built into the interpreter.
const MISC; #[derive(Clone, Copy)]
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
pub fn format<_, args> () -> IResult<ConValue> { fn fmt(args @ ..) {
use std::fmt::Write; use std::fmt::Write;
let mut out = String::new(); let mut out = String::new();
for arg in args { if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
write!(out, "{arg}").ok(); eprintln!("{e}");
} }
Ok(ConValue::String(out.into())) Ok(out)
} }
/// Unstable variadic print function /// Prints the arguments in-order, with no separators
pub fn print<_, args> () -> IResult<ConValue> { fn print(args @ ..) {
let mut out = stdout().lock(); let mut out = stdout().lock();
for arg in args { args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
write!(out, "{arg}").ok(); Ok(())
}
Ok(ConValue::Empty)
} }
/// Unstable variadic println function /// Prints the arguments in-order, followed by a newline
pub fn println<_, args> () -> IResult<ConValue> { fn println(args @ ..) {
let mut out = stdout().lock(); let mut out = stdout().lock();
for arg in args { args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
write!(out, "{arg}").ok();
}
writeln!(out).ok(); writeln!(out).ok();
Ok(ConValue::Empty) Ok(())
} }
/// Prints the [Debug](std::fmt::Debug) version of the input values /// Debug-prints the argument, returning a copy
pub fn dbg<_, args> () -> IResult<ConValue> { fn dbg(arg) {
println!("{arg:?}");
Ok(arg.clone())
}
/// Debug-prints the argument
fn dbgp(args @ ..) {
let mut out = stdout().lock(); let mut out = stdout().lock();
for arg in args { args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
writeln!(out, "{arg:?}").ok(); Ok(())
}
/// Dumps the environment
fn dump() @env {
println!("{env}");
Ok(())
}
fn builtins() @env {
for builtin in env.builtins().values().flatten() {
println!("{builtin}");
} }
Ok(args.into()) Ok(())
} }
/// Dumps info from the environment /// Returns the length of the input list as a [ConValue::Int]
pub fn dump<env, _>() -> IResult<ConValue> { fn len(list) @env {
println!("{}", *env); Ok(match list {
Ok(ConValue::Empty)
}
pub fn len<env, _>(list) -> IResult<ConValue> {
Ok(ConValue::Int(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.call(env, slice::from_ref(r.as_ref())), ConValue::Ref(r) => return len(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)?,
})) })
} }
}
builtins! { fn dump_symbols() {
const BINARY; println!("{}", cl_structures::intern::string_interner::StringInterner::global());
Ok(ConValue::Empty)
}
/// Returns a shark
fn shark() {
Ok('\u{1f988}')
}
];
pub const Math: &[Builtin] = &builtins![
/// Multiplication `a * b` /// Multiplication `a * b`
pub fn mul(lhs, rhs) -> IResult<ConValue> { fn mul(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
@@ -84,7 +195,7 @@ builtins! {
} }
/// Division `a / b` /// Division `a / b`
pub fn div(lhs, rhs) -> IResult<ConValue> { fn div(lhs, rhs) {
Ok(match (lhs, rhs){ Ok(match (lhs, rhs){
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
@@ -93,7 +204,7 @@ builtins! {
} }
/// Remainder `a % b` /// Remainder `a % b`
pub fn rem(lhs, rhs) -> IResult<ConValue> { fn rem(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
@@ -102,7 +213,7 @@ builtins! {
} }
/// Addition `a + b` /// Addition `a + b`
pub fn add(lhs, rhs) -> IResult<ConValue> { fn add(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
@@ -112,7 +223,7 @@ builtins! {
} }
/// Subtraction `a - b` /// Subtraction `a - b`
pub fn sub(lhs, rhs) -> IResult<ConValue> { fn sub(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
@@ -121,7 +232,7 @@ builtins! {
} }
/// Shift Left `a << b` /// Shift Left `a << b`
pub fn shl(lhs, rhs) -> IResult<ConValue> { fn shl(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
@@ -130,7 +241,7 @@ builtins! {
} }
/// Shift Right `a >> b` /// Shift Right `a >> b`
pub fn shr(lhs, rhs) -> IResult<ConValue> { fn shr(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
@@ -139,7 +250,7 @@ builtins! {
} }
/// Bitwise And `a & b` /// Bitwise And `a & b`
pub fn and(lhs, rhs) -> IResult<ConValue> { fn and(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
@@ -149,7 +260,7 @@ builtins! {
} }
/// Bitwise Or `a | b` /// Bitwise Or `a | b`
pub fn or(lhs, rhs) -> IResult<ConValue> { fn or(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
@@ -159,7 +270,7 @@ builtins! {
} }
/// Bitwise Exclusive Or `a ^ b` /// Bitwise Exclusive Or `a ^ b`
pub fn xor(lhs, rhs) -> IResult<ConValue> { fn xor(lhs, rhs) {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
@@ -168,67 +279,34 @@ builtins! {
}) })
} }
/// Tests whether `a < b`
pub fn lt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, <)
}
/// Tests whether `a <= b`
pub fn lt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, <=)
}
/// Tests whether `a == b`
pub fn eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, ==)
}
/// Tests whether `a != b`
pub fn neq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, !=)
}
/// Tests whether `a <= b`
pub fn gt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, >=)
}
/// Tests whether `a < b`
pub fn gt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, >)
}
}
builtins! {
const RANGE;
/// Exclusive Range `a..b` /// Exclusive Range `a..b`
pub fn range_exc(lhs, rhs) -> IResult<ConValue> { fn range_exc(from, to) {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else { let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
Err(Error::TypeError)? Err(Error::TypeError)?
}; };
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1))) Ok(ConValue::RangeExc(from, to))
} }
/// Inclusive Range `a..=b` /// Inclusive Range `a..=b`
pub fn range_inc(lhs, rhs) -> IResult<ConValue> { fn range_inc(from, to) {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else { let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
Err(Error::TypeError)? Err(Error::TypeError)?
}; };
Ok(ConValue::RangeInc(lhs, rhs)) Ok(ConValue::RangeInc(from, to))
} }
}
builtins! {
const UNARY;
/// Negates the ConValue /// Negates the ConValue
pub fn neg(tail) -> IResult<ConValue> { fn neg(tail) {
Ok(match tail { Ok(match tail {
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()), ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
ConValue::Float(v) => ConValue::Float(-v),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError)?,
}) })
} }
/// Inverts the ConValue /// Inverts the ConValue
pub fn not(tail) -> IResult<ConValue> { fn not(tail) {
Ok(match tail { Ok(match tail {
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(!v), ConValue::Int(v) => ConValue::Int(!v),
@@ -237,62 +315,23 @@ builtins! {
}) })
} }
pub fn deref(tail) -> IResult<ConValue> { /// Compares two values
fn cmp(head, tail) {
Ok(ConValue::Int(match (head, tail) {
(ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
(ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
(ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
(ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
_ => Err(error_format!("Incomparable values: {head}, {tail}"))?
}))
}
/// Does the opposite of `&`
fn deref(tail) {
use std::rc::Rc;
Ok(match tail { Ok(match tail {
ConValue::Ref(v) => Rc::as_ref(v).clone(), ConValue::Ref(v) => Rc::as_ref(v).clone(),
_ => tail.clone(), _ => tail.clone(),
}) })
} }
} ];
/// Turns an argument slice into an array with the (inferred) correct number of elements
pub fn to_args<const N: usize>(args: &[ConValue]) -> IResult<&[ConValue; N]> {
args.try_into()
.map_err(|_| Error::ArgNumber { want: N, got: args.len() })
}
/// Turns function definitions into ZSTs which implement [Callable] and [BuiltIn]
macro builtins (
$(prefix = $prefix:literal)?
const $defaults:ident $( = [$($additional_builtins:expr),*$(,)?])?;
$(
$(#[$meta:meta])*$vis:vis fn $name:ident$(<$env:tt, $args:tt>)? ( $($($arg:tt),+$(,)?)? ) $(-> $rety:ty)?
$body:block
)*
) {
/// Builtins to load when a new interpreter is created
pub const $defaults: &[&dyn BuiltIn] = &[$(&$name,)* $($additional_builtins)*];
$(
$(#[$meta])* #[allow(non_camel_case_types)] #[derive(Clone, Debug)]
/// ```rust,ignore
#[doc = stringify!(builtin! fn $name($($($arg),*)?) $(-> $rety)? $body)]
/// ```
$vis struct $name;
impl BuiltIn for $name {
fn description(&self) -> &str { concat!("builtin ", stringify!($name), stringify!(($($($arg),*)?) )) }
}
impl Callable for $name {
#[allow(unused)]
fn call(&self, env: &mut Environment, args: &[ConValue]) $(-> $rety)? {
// println!("{}", stringify!($name), );
$(let $env = env;
let $args = args;)?
$(let [$($arg),*] = to_args(args)?;)?
$body
}
fn name(&self) -> Sym { stringify!($name).into() }
}
)*
}
/// Templates comparison functions for [ConValue]
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
match ($a, $b) {
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(&**a $op &**b)),
_ => Err(Error::TypeError)
}
}

View File

@@ -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::Sym; use cl_ast::{format::FmtAdapter, ExprKind, Sym};
use super::{ use super::{
builtin::Builtin,
error::{Error, IResult}, error::{Error, IResult},
function::Function, function::Function, Callable, Environment,
BuiltIn, Callable, Environment,
}; };
use std::{ops::*, rc::Rc}; use std::{collections::HashMap, ops::*, rc::Rc};
type Integer = isize; type Integer = isize;
@@ -20,6 +20,8 @@ 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
@@ -29,18 +31,25 @@ pub enum ConValue {
/// A reference /// A reference
Ref(Rc<ConValue>), Ref(Rc<ConValue>),
/// An Array /// An Array
Array(Rc<[ConValue]>), Array(Box<[ConValue]>),
/// A tuple /// A tuple
Tuple(Rc<[ConValue]>), Tuple(Box<[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(Function), Function(Rc<Function>),
/// A built-in function /// A built-in function
BuiltIn(&'static dyn BuiltIn), Builtin(&'static 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> {
@@ -53,7 +62,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.saturating_sub(1))) Ok(Self::RangeExc(a, b))
} }
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 {
@@ -104,14 +113,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())),
} }
} }
@@ -124,6 +133,7 @@ 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)),
@@ -150,15 +160,17 @@ 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 dyn BuiltIn => ConValue::BuiltIn, &'static Builtin => ConValue::Builtin,
} }
impl From<()> for ConValue { impl From<()> for ConValue {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
@@ -189,7 +201,8 @@ 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::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() } (ConValue::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())
@@ -219,18 +232,21 @@ 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(|| {
eprintln!("Warning: Divide by zero in {a} % {b}"); a println!("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 = [
@@ -246,6 +262,7 @@ 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)?
] ]
} }
@@ -254,6 +271,7 @@ 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),
@@ -280,10 +298,37 @@ 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,16 +1,20 @@
//! Lexical and non-lexical scoping for variables //! Lexical and non-lexical scoping for variables
use crate::builtin::Builtin;
use super::{ use super::{
builtin::{BINARY, MISC, RANGE, UNARY}, builtin::{Builtins, Math},
convalue::ConValue, convalue::ConValue,
error::{Error, IResult}, error::{Error, IResult},
function::Function, function::Function,
BuiltIn, Callable, Interpret, 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>>;
@@ -18,12 +22,21 @@ 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.frames.iter().rev() { for (frame, name) in self
.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}: ")?;
@@ -39,18 +52,17 @@ impl Display for Environment {
impl Default for Environment { impl Default for Environment {
fn default() -> Self { fn default() -> Self {
Self { Self {
frames: vec![ builtin: to_hashmap(Builtins.iter().chain(Math.iter())),
(to_hashmap(RANGE), "range ops"), global: vec![(HashMap::new(), "globals")],
(to_hashmap(UNARY), "unary ops"), frames: vec![],
(to_hashmap(BINARY), "binary ops"),
(to_hashmap(MISC), "builtins"),
(HashMap::new(), "globals"),
],
} }
} }
} }
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<Sym, Option<ConValue>> {
from.iter().map(|&v| (v.name(), Some(v.into()))).collect() fn to_hashmap(from: impl IntoIterator<Item = &'static Builtin>) -> HashMap<Sym, Option<ConValue>> {
from.into_iter()
.map(|v| (v.name(), Some(v.into())))
.collect()
} }
impl Environment { impl Environment {
@@ -58,8 +70,35 @@ 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(name: &'static str) -> Self { pub fn no_builtins() -> Self {
Self { frames: vec![(Default::default(), name)] } Self {
builtin: HashMap::new(),
global: vec![(Default::default(), "globals")],
frames: vec![],
}
}
pub fn builtins(&self) -> &StackFrame {
&self.builtin
}
pub fn add_builtin(&mut self, builtin: &'static Builtin) -> &mut Self {
self.builtin.insert(builtin.name(), Some(builtin.into()));
self
}
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
for builtin in builtins {
self.add_builtin(builtin);
}
}
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
self.frames.push((frame, name));
}
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
self.frames.pop()
} }
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> { pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
@@ -88,7 +127,12 @@ impl Environment {
return Ok(var); return Ok(var);
} }
} }
Err(Error::NotDefined(id)) for (frame, _) in self.global.iter_mut().rev() {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
self.builtin.get_mut(&id).ok_or(Error::NotDefined(id))
} }
/// Resolves a variable immutably. /// Resolves a variable immutably.
/// ///
@@ -101,21 +145,50 @@ impl Environment {
_ => (), _ => (),
} }
} }
Err(Error::NotDefined(id)) for (frame, _) in self.global.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
self.builtin
.get(&id)
.cloned()
.flatten()
.ok_or(Error::NotDefined(id))
} }
pub(crate) fn get_local(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotInitialized(id))
}
/// Inserts a new [ConValue] into this [Environment] /// 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, Some(Function::new(decl).into())); let (name, function) = (name, Rc::new(Function::new(decl)));
if let Some((frame, _)) = self.frames.last_mut() { if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(*name, function); frame.insert(*name, Some(ConValue::Function(function.clone())));
} else if let Some((frame, _)) = self.global.last_mut() {
frame.insert(*name, Some(ConValue::Function(function.clone())));
} }
// Tell the function to lift its upvars now, after it's been declared
function.lift_upvars(self);
} }
} }
@@ -130,9 +203,7 @@ 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
} }
} }
@@ -147,18 +218,18 @@ impl<'scope> Frame<'scope> {
Self { scope: scope.enter(name) } Self { scope: scope.enter(name) }
} }
} }
impl<'scope> Deref for Frame<'scope> { impl Deref for Frame<'_> {
type Target = Environment; type Target = Environment;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.scope self.scope
} }
} }
impl<'scope> DerefMut for Frame<'scope> { impl DerefMut for Frame<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.scope self.scope
} }
} }
impl<'scope> Drop for Frame<'scope> { impl Drop for Frame<'_> {
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::Sym; use cl_ast::{Pattern, Sym};
use super::convalue::ConValue; use super::convalue::ConValue;
@@ -39,11 +39,13 @@ 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 { ArgNumber { want: usize, got: usize },
want: usize, /// A pattern failed to match
got: usize, PatFailed(Box<Pattern>),
}, /// Fell through a non-exhaustive match
Outlined(Sym), MatchNonexhaustive,
/// Error produced by a Builtin
BuiltinDebug(String),
} }
impl std::error::Error for Error {} impl std::error::Error for Error {}
@@ -83,9 +85,13 @@ impl std::fmt::Display for Error {
if *want == 1 { "" } else { "s" } if *want == 1 { "" } else { "s" }
) )
} }
Error::Outlined(name) => { Error::PatFailed(pattern) => {
write!(f, "Module {name} specified, but not imported.") write!(f, "Failed to match pattern {pattern}")
} }
Error::MatchNonexhaustive => {
write!(f, "Fell through a non-exhaustive match expression!")
}
Error::BuiltinDebug(s) => write!(f, "DEBUG: {s}"),
} }
} }
} }

View File

@@ -1,24 +1,45 @@
//! 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::rc::Rc; use std::{
cell::{Ref, RefCell},
collections::HashMap,
rc::Rc,
};
pub mod collect_upvars;
type Upvars = HashMap<Sym, Option<ConValue>>;
/// Represents a block of code which persists inside the Interpreter /// 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 the enclosing scope of the function /// Stores data from the enclosing scopes
// env: Box<Environment>, upvars: RefCell<Upvars>,
} }
impl Function { impl Function {
pub fn new(decl: &FnDecl) -> Self { pub fn new(decl: &FnDecl) -> Self {
Self { decl: decl.clone().into() } // let upvars = collect_upvars(decl, env);
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 {
@@ -28,6 +49,7 @@ 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() });
@@ -35,12 +57,21 @@ 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()));
} }
match body.interpret(&mut frame) { let res = body.interpret(&mut frame);
drop(frame);
if let Some((upvars, _)) = env.pop_frame() {
self.upvars.replace(upvars);
}
match res {
Err(Error::Return(value)) => Ok(value), Err(Error::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

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

View File

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

View File

@@ -17,11 +17,6 @@ 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::{env::Environment, convalue::ConValue, Interpret}; use crate::{convalue::ConValue, env::Environment, Interpret};
use cl_ast::*; use cl_ast::*;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
@@ -178,6 +178,45 @@ mod let_declarations {
env_eq!(env.x, 10); env_eq!(env.x, 10);
env_eq!(env.y, 10); env_eq!(env.y, 10);
} }
#[test]
fn let_destructuring_tuple() {
let mut env = Environment::new();
assert_eval!(env,
let (x, y) = (10, 20);
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_array() {
let mut env = Environment::new();
assert_eval!(env,
let [x, y] = [10, 20];
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_nested() {
let mut env = Environment::new();
assert_eval!(env,
let (x, [one, two, three], (a, b, c))
= ('x', [1, 2, 3], ('a', 'b', 'c'));
);
env_eq!(env.x, 'x');
env_eq!(env.one, 1);
env_eq!(env.two, 2);
env_eq!(env.three, 3);
env_eq!(env.a, 'a');
env_eq!(env.b, 'b');
env_eq!(env.c, 'c');
}
} }
mod fn_declarations { mod fn_declarations {
@@ -188,7 +227,7 @@ mod fn_declarations {
assert_eval!(env, fn empty_fn() {}); assert_eval!(env, fn empty_fn() {});
// TODO: true equality for functions // TODO: true equality for functions
assert_eq!( assert_eq!(
"fn empty_fn () {\n \n}", "fn empty_fn () {}",
format!( format!(
"{}", "{}",
env.get("empty_fn".into()) env.get("empty_fn".into())
@@ -437,16 +476,17 @@ mod operators {
env_eq!(env.y, 10); env_eq!(env.y, 10);
env_eq!(env.z, 10); env_eq!(env.z, 10);
} }
#[test] // Test is disabled, since new assignment system intentionally does not care.
#[should_panic] // #[test]
fn assignment_accounts_for_type() { // #[should_panic]
let mut env = Default::default(); // fn assignment_accounts_for_type() {
assert_eval!(env, // let mut env = Default::default();
let x = "a string"; // assert_eval!(env,
let y = 0xdeadbeef; // let x = "a string";
y = x; // should crash: type error // let y = 0xdeadbeef;
); // y = x; // should crash: type error
} // );
// }
#[test] #[test]
fn precedence() { fn precedence() {
let mut env = Default::default(); let mut env = Default::default();
@@ -469,6 +509,56 @@ mod operators {
} }
} }
mod control_flow {
use super::*;
#[test]
fn if_evaluates_pass_block_on_true() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if true { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "pass");
}
#[test]
fn if_evaluates_fail_block_on_false() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if false { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "fail");
}
#[test]
fn match_evaluates_in_order() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
'\u{1f988}' => true,
_ => false,
};
);
env_eq!(env.passed, true);
}
#[test]
fn match_sinkoles_underscore_patterns() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
_ => true,
'\u{1f988}' => false,
};
);
env_eq!(env.passed, true);
}
//TODO: test other control flow constructs like loops, while-else, etc.
}
#[allow(dead_code)] #[allow(dead_code)]
fn test_template() { fn test_template() {
let mut env = Default::default(); let mut env = Default::default();

View File

@@ -23,7 +23,7 @@ pub mod lexer_iter {
pub struct LexerIter<'t> { pub struct LexerIter<'t> {
lexer: Lexer<'t>, lexer: Lexer<'t>,
} }
impl<'t> Iterator for LexerIter<'t> { impl Iterator for LexerIter<'_> {
type Item = LResult<Token>; type Item = LResult<Token>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.lexer.scan() { match self.lexer.scan() {
@@ -192,7 +192,7 @@ impl<'t> Lexer<'t> {
} }
} }
/// Digraphs and trigraphs /// Digraphs and trigraphs
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn amp(&mut self) -> LResult<Token> { fn amp(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('&') => self.consume()?.produce_op(Kind::AmpAmp), Ok('&') => self.consume()?.produce_op(Kind::AmpAmp),
@@ -319,7 +319,7 @@ impl<'t> Lexer<'t> {
} }
} }
/// Comments /// Comments
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn line_comment(&mut self) -> LResult<Token> { fn line_comment(&mut self) -> LResult<Token> {
let mut comment = String::new(); let mut comment = String::new();
while Ok('\n') != self.peek() { while Ok('\n') != self.peek() {
@@ -339,7 +339,7 @@ impl<'t> Lexer<'t> {
} }
} }
/// Identifiers /// Identifiers
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn identifier(&mut self) -> LResult<Token> { fn identifier(&mut self) -> LResult<Token> {
let mut out = String::from(self.xid_start()?); let mut out = String::from(self.xid_start()?);
while let Ok(c) = self.xid_continue() { while let Ok(c) = self.xid_continue() {
@@ -371,23 +371,39 @@ impl<'t> Lexer<'t> {
} }
} }
/// Integers /// Integers
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn int_with_base(&mut self) -> LResult<Token> { fn int_with_base(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('x') => self.consume()?.digits::<16>(), Ok('x') => self.consume()?.digits::<16>(),
Ok('d') => self.consume()?.digits::<10>(), Ok('d') => self.consume()?.digits::<10>(),
Ok('o') => self.consume()?.digits::<8>(), Ok('o') => self.consume()?.digits::<8>(),
Ok('b') => self.consume()?.digits::<2>(), Ok('b') => self.consume()?.digits::<2>(),
Ok('0'..='9') => self.digits::<10>(), Ok('0'..='9' | '.') => self.digits::<10>(),
_ => self.produce(Kind::Literal, 0), _ => self.produce(Kind::Literal, 0),
} }
} }
fn digits<const B: u32>(&mut self) -> LResult<Token> { fn digits<const B: u32>(&mut self) -> LResult<Token> {
let mut value = self.digit::<B>()? as u128; let mut value = 0;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) { while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
value = value * B as u128 + self.digit::<B>()? as u128; value = value * B as u128 + self.digit::<B>()? as u128;
} }
self.produce(Kind::Literal, value) // TODO: find a better way to handle floats in the tokenizer
match self.peek() {
Ok('.') => {
// FIXME: hack: 0.. is not [0.0, '.']
if let Ok('.') = self.clone().consume()?.next() {
return self.produce(Kind::Literal, value);
}
let mut float = format!("{value}.");
self.consume()?;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_digit) {
float.push(self.iter.next().unwrap_or_default());
}
let float = f64::from_str(&float).expect("must be parsable as float");
self.produce(Kind::Literal, float)
}
_ => self.produce(Kind::Literal, value),
}
} }
fn digit<const B: u32>(&mut self) -> LResult<u32> { fn digit<const B: u32>(&mut self) -> LResult<u32> {
let digit = self.peek()?; let digit = self.peek()?;
@@ -398,7 +414,7 @@ impl<'t> Lexer<'t> {
} }
} }
/// Strings and characters /// Strings and characters
impl<'t> Lexer<'t> { impl Lexer<'_> {
fn string(&mut self) -> LResult<Token> { fn string(&mut self) -> LResult<Token> {
let mut value = String::new(); let mut value = String::new();
while '"' while '"'

View File

@@ -119,6 +119,10 @@ pub enum Parsing {
Break, Break,
Return, Return,
Continue, Continue,
Pattern,
Match,
MatchArm,
} }
impl Display for Error { impl Display for Error {
@@ -225,6 +229,10 @@ impl Display for Parsing {
Parsing::Break => "a break expression", Parsing::Break => "a break expression",
Parsing::Return => "a return expression", Parsing::Return => "a return expression",
Parsing::Continue => "a continue expression", Parsing::Continue => "a continue expression",
Parsing::Pattern => "a pattern",
Parsing::Match => "a match expression",
Parsing::MatchArm => "a match arm",
} }
.fmt(f) .fmt(f)
} }

View File

@@ -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) => todo!("Literal::Float({v})"), TokenData::Float(v) => Literal::Float(v.to_bits()),
_ => panic!("Expected token data for {ty:?}"), _ => panic!("Expected token data for {ty:?}"),
}) })
} }
@@ -681,7 +681,11 @@ impl Parse<'_> for UseTree {
let PathPart::Ident(name) = name else { let PathPart::Ident(name) = name else {
Err(p.error(ErrorKind::ExpectedParsing { want: Parsing::Identifier }, P))? Err(p.error(ErrorKind::ExpectedParsing { want: Parsing::Identifier }, P))?
}; };
UseTree::Name(name) if p.match_type(TokenKind::As, P).is_ok() {
UseTree::Alias(name, p.parse()?)
} else {
UseTree::Name(name)
}
} }
} }
t => Err(p.error(Unexpected(t), Parsing::UseTree))?, t => Err(p.error(Unexpected(t), Parsing::UseTree))?,
@@ -900,12 +904,24 @@ 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: Sym::parse(p)?, name: Pattern::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 {
@@ -962,16 +978,24 @@ 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;
let mut count = 0; match p.peek_kind(P)? {
loop { TokenKind::Amp => {
count += match p.peek_kind(P)? { p.consume_peeked();
TokenKind::Amp => 1, Ok(AddrOf { mutable: Mutability::parse(p)?, expr: ExprKind::parse(p)?.into() })
TokenKind::AmpAmp => 2, }
_ => break, TokenKind::AmpAmp => {
}; p.consume_peeked();
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() })
} }
} }
@@ -1055,6 +1079,38 @@ 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,10 +16,12 @@ 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)?),
@@ -31,8 +33,7 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
} }
op => { op => {
let (kind, prec) = let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
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()
@@ -64,14 +65,10 @@ 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 = let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
p.match_type(TokenKind::RParen, parsing)?; p.match_type(TokenKind::RParen, parsing)?;
Binary { Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() }
kind: BinaryKind::Call, .into()
parts: (head, Tuple { exprs }.into()).into(),
}
.into()
} }
TokenKind::Dot => { TokenKind::Dot => {
let kind = MemberKind::parse(p)?; let kind = MemberKind::parse(p)?;
@@ -247,9 +244,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,
@@ -259,6 +256,7 @@ pub enum Precedence {
Cast, Cast,
Member, // left-associative Member, // left-associative
Call, Call,
Highest,
} }
impl Precedence { impl Precedence {
@@ -309,9 +307,7 @@ 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 => { Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
Precedence::Compare
}
} }
} }
} }

View File

@@ -120,19 +120,19 @@ pub mod yamler {
} }
} }
impl<'y> Deref for Section<'y> { impl Deref for Section<'_> {
type Target = Yamler; type Target = Yamler;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.yamler self.yamler
} }
} }
impl<'y> DerefMut for Section<'y> { impl DerefMut for Section<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.yamler self.yamler
} }
} }
impl<'y> Drop for Section<'y> { impl Drop for Section<'_> {
fn drop(&mut self) { fn drop(&mut self) {
let Self { yamler } = self; let Self { yamler } = self;
yamler.decrease(); yamler.decrease();
@@ -369,16 +369,6 @@ 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;
@@ -388,7 +378,9 @@ 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),
@@ -417,6 +409,60 @@ 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;
@@ -525,11 +571,8 @@ pub mod yamlify {
} }
impl Yamlify for AddrOf { impl Yamlify for AddrOf {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { count, mutable, expr } = self; let Self { mutable, expr } = self;
y.key("AddrOf") y.key("AddrOf").yaml(mutable).pair("expr", expr);
.pair("count", count)
.yaml(mutable)
.pair("expr", expr);
} }
} }
impl Yamlify for Group { impl Yamlify for Group {

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::{convalue::ConValue, env::Environment, interpret::Interpret}; use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use std::{error::Error, path::Path}; use std::{error::Error, path::Path};
@@ -16,6 +16,31 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
let Args { file, include, mode, repl } = args; let Args { file, include, mode, repl } = args;
let mut env = Environment::new(); let mut env = Environment::new();
env.add_builtins(&builtins! {
/// Clears the screen
fn clear() {
menu::clear();
Ok(ConValue::Empty)
}
/// Evaluates a quoted expression
fn eval(ConValue::Quote(quote)) @env {
env.eval(quote.as_ref())
}
/// Executes a file
fn import(ConValue::String(path)) @env {
load_file(env, &**path).or(Ok(ConValue::Empty))
}
/// Gets a line of input from stdin
fn get_line() {
match repline::Repline::new("", "", "").read() {
Ok(line) => Ok(ConValue::String(line.into())),
Err(e) => Ok(ConValue::String(e.to_string().into())),
}
}
});
for path in include { for path in include {
load_file(&mut env, path)?; load_file(&mut env, path)?;
} }

View File

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

View File

@@ -1,11 +1,12 @@
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::*};
fn clear() { pub fn clear() {
println!("{}", ansi::CLEAR_ALL); print!("{}", ansi::CLEAR_ALL);
banner() banner()
} }
@@ -43,11 +44,15 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
use cl_parser::inliner::ModuleInliner; use cl_parser::inliner::ModuleInliner;
read_and(ansi::CYAN, "cl>", " ?>", |line| { read_and(ansi::CYAN, "cl>", " ?>", |line| {
if line.trim().is_empty() {
return Ok(Response::Deny);
}
let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?; let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?;
let code = ModuleInliner::new(".").fold_stmt(code); let code = ModuleInliner::new(".").fold_stmt(code);
print!("{}", ansi::OUTPUT); print!("{}", ansi::OUTPUT);
match ctx.run(&code) { match ctx.run(&code) {
Ok(ConValue::Empty) => print!("{}", ansi::RESET),
Ok(v) => println!("{}{v}", ansi::RESET), Ok(v) => println!("{}{v}", ansi::RESET),
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET), Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
} }

View File

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

View File

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

View File

@@ -35,9 +35,14 @@ pub mod interned {
pub fn as_ptr(interned: &Self) -> *const T { pub fn as_ptr(interned: &Self) -> *const T {
interned.value interned.value
} }
/// Gets the internal value as a reference with the interner's lifetime
pub fn to_ref(interned: &Self) -> &'a T {
interned.value
}
} }
impl<'a, T: ?Sized + Debug> Debug for Interned<'a, T> { impl<T: ?Sized + Debug> Debug for Interned<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Interned") f.debug_struct("Interned")
.field("value", &self.value) .field("value", &self.value)
@@ -49,14 +54,14 @@ pub mod interned {
Self { value } Self { value }
} }
} }
impl<'a, T: ?Sized> Deref for Interned<'a, T> { impl<T: ?Sized> Deref for Interned<'_, T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.value self.value
} }
} }
impl<'a, T: ?Sized> Copy for Interned<'a, T> {} impl<T: ?Sized> Copy for Interned<'_, T> {}
impl<'a, T: ?Sized> Clone for Interned<'a, T> { impl<T: ?Sized> Clone for Interned<'_, T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self
} }
@@ -79,13 +84,13 @@ pub mod interned {
// } // }
// } // }
impl<'a, T: ?Sized> Eq for Interned<'a, T> {} impl<T: ?Sized> Eq for Interned<'_, T> {}
impl<'a, T: ?Sized> PartialEq for Interned<'a, T> { impl<T: ?Sized> PartialEq for Interned<'_, T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.value, other.value) std::ptr::eq(self.value, other.value)
} }
} }
impl<'a, T: ?Sized> Hash for Interned<'a, T> { impl<T: ?Sized> Hash for Interned<'_, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Self::as_ptr(self).hash(state) Self::as_ptr(self).hash(state)
} }
@@ -119,6 +124,7 @@ pub mod string_interner {
}; };
/// A string interner hands out [Interned] copies of each unique string given to it. /// A string interner hands out [Interned] copies of each unique string given to it.
#[derive(Default)]
pub struct StringInterner<'a> { pub struct StringInterner<'a> {
arena: DroplessArena<'a>, arena: DroplessArena<'a>,
keys: RwLock<HashSet<&'a str>>, keys: RwLock<HashSet<&'a str>>,
@@ -207,8 +213,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<'a> Send for StringInterner<'a> {} unsafe impl Send for StringInterner<'_> {}
unsafe impl<'a> Sync for StringInterner<'a> {} unsafe impl Sync for StringInterner<'_> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@@ -258,6 +264,7 @@ pub mod typed_interner {
/// A [TypedInterner] hands out [Interned] references for arbitrary types. /// A [TypedInterner] hands out [Interned] references for arbitrary types.
/// ///
/// See the [module-level documentation](self) for more information. /// See the [module-level documentation](self) for more information.
#[derive(Default)]
pub struct TypedInterner<'a, T: Eq + Hash> { pub struct TypedInterner<'a, T: Eq + Hash> {
arena: TypedArena<'a, T>, arena: TypedArena<'a, T>,
keys: RwLock<HashSet<&'a T>>, keys: RwLock<HashSet<&'a T>>,
@@ -304,5 +311,5 @@ pub mod typed_interner {
/// [get_or_insert](TypedInterner::get_or_insert) are unique, and the function uses /// [get_or_insert](TypedInterner::get_or_insert) are unique, and the function uses
/// the [RwLock] around the [HashSet] to ensure mutual exclusion /// the [RwLock] around the [HashSet] to ensure mutual exclusion
unsafe impl<'a, T: Eq + Hash + Send> Send for TypedInterner<'a, T> where &'a T: Send {} unsafe impl<'a, T: Eq + Hash + Send> Send for TypedInterner<'a, T> where &'a T: Send {}
unsafe impl<'a, T: Eq + Hash + Send + Sync> Sync for TypedInterner<'a, T> {} unsafe impl<T: Eq + Hash + Send + Sync> Sync for TypedInterner<'_, T> {}
} }

View File

@@ -28,6 +28,7 @@ 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"
@@ -106,34 +107,35 @@ impl Display for TokenKind {
TokenKind::Literal => "literal".fmt(f), TokenKind::Literal => "literal".fmt(f),
TokenKind::Identifier => "identifier".fmt(f), TokenKind::Identifier => "identifier".fmt(f),
TokenKind::As => "as".fmt(f), TokenKind::As => "sama".fmt(f),
TokenKind::Break => "break".fmt(f), TokenKind::Break => "pana".fmt(f),
TokenKind::Cl => "cl".fmt(f), TokenKind::Cl => "la".fmt(f),
TokenKind::Const => "const".fmt(f), TokenKind::Const => "kiwen".fmt(f),
TokenKind::Continue => "continue".fmt(f), TokenKind::Continue => "tawa".fmt(f),
TokenKind::Else => "else".fmt(f), TokenKind::Else => "taso".fmt(f),
TokenKind::Enum => "enum".fmt(f), TokenKind::Enum => "kulupu".fmt(f),
TokenKind::False => "false".fmt(f), TokenKind::False => "ike".fmt(f),
TokenKind::Fn => "fn".fmt(f), TokenKind::Fn => "nasin".fmt(f),
TokenKind::For => "for".fmt(f), TokenKind::For => "ale".fmt(f),
TokenKind::If => "if".fmt(f), TokenKind::If => "tan".fmt(f),
TokenKind::Impl => "impl".fmt(f), TokenKind::Impl => "insa".fmt(f),
TokenKind::In => "in".fmt(f), TokenKind::In => "lon".fmt(f),
TokenKind::Let => "let".fmt(f), TokenKind::Let => "poki".fmt(f),
TokenKind::Loop => "loop".fmt(f), TokenKind::Loop => "awen".fmt(f),
TokenKind::Mod => "mod".fmt(f), TokenKind::Match => "seme".fmt(f),
TokenKind::Mut => "mut".fmt(f), TokenKind::Mod => "selo".fmt(f),
TokenKind::Pub => "pub".fmt(f), TokenKind::Mut => "ante".fmt(f),
TokenKind::Return => "return".fmt(f), TokenKind::Pub => "lukin".fmt(f),
TokenKind::SelfKw => "self".fmt(f), TokenKind::Return => "pini".fmt(f),
TokenKind::SelfTy => "Self".fmt(f), TokenKind::SelfKw => "mi".fmt(f),
TokenKind::Static => "static".fmt(f), TokenKind::SelfTy => "Mi".fmt(f),
TokenKind::Struct => "struct".fmt(f), TokenKind::Static => "mute".fmt(f),
TokenKind::Super => "super".fmt(f), TokenKind::Struct => "lipu".fmt(f),
TokenKind::True => "true".fmt(f), TokenKind::Super => "mama".fmt(f),
TokenKind::Type => "type".fmt(f), TokenKind::True => "pona".fmt(f),
TokenKind::Use => "use".fmt(f), TokenKind::Type => "ijo".fmt(f),
TokenKind::While => "while".fmt(f), TokenKind::Use => "jo".fmt(f),
TokenKind::While => "lawa".fmt(f),
TokenKind::LCurly => "{".fmt(f), TokenKind::LCurly => "{".fmt(f),
TokenKind::RCurly => "}".fmt(f), TokenKind::RCurly => "}".fmt(f),
@@ -198,34 +200,35 @@ impl FromStr for TokenKind {
/// Parses a string s to return a Keyword /// Parses a string s to return a Keyword
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s { Ok(match s {
"as" => Self::As, "as" | "sama" => Self::As,
"break" => Self::Break, "break" | "pana" => Self::Break,
"cl" => Self::Cl, "cl" | "la" => Self::Cl,
"const" => Self::Const, "const" | "kiwen" => Self::Const,
"continue" => Self::Continue, "continue" | "tawa" => Self::Continue,
"else" => Self::Else, "else" | "taso" => Self::Else,
"enum" => Self::Enum, "enum" | "kulupu" => Self::Enum,
"false" => Self::False, "false" | "ike" => Self::False,
"fn" => Self::Fn, "fn" | "nasin" => Self::Fn,
"for" => Self::For, "for" | "ale" => Self::For,
"if" => Self::If, "if" | "tan" => Self::If,
"impl" => Self::Impl, "impl" | "insa" => Self::Impl,
"in" => Self::In, "in" | "lon" => Self::In,
"let" => Self::Let, "let" | "poki" => Self::Let,
"loop" => Self::Loop, "loop" | "awen" => Self::Loop,
"mod" => Self::Mod, "match" | "seme" => Self::Match,
"mut" => Self::Mut, "mod" | "selo" => Self::Mod,
"pub" => Self::Pub, "mut" | "ante" => Self::Mut,
"return" => Self::Return, "pub" | "lukin" => Self::Pub,
"self" => Self::SelfKw, "return" | "pini" => Self::Return,
"Self" => Self::SelfTy, "self" | "mi" => Self::SelfKw,
"static" => Self::Static, "Self" | "Mi" => Self::SelfTy,
"struct" => Self::Struct, "static" | "mute" => Self::Static,
"super" => Self::Super, "struct" | "lipu" => Self::Struct,
"true" => Self::True, "super" | "mama" => Self::Super,
"type" => Self::Type, "true" | "pona" => Self::True,
"use" => Self::Use, "type" | "ijo" => Self::Type,
"while" => Self::While, "use" | "jo" => Self::Use,
"while" | "lawa" => Self::While,
_ => Err(())?, _ => Err(())?,
}) })
} }

View File

@@ -1,4 +1,3 @@
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::{
@@ -8,10 +7,12 @@ 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
@@ -30,11 +31,6 @@ 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();
@@ -48,7 +44,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(unsafe { TREES.push(code) }); Populator::new(&mut prj).visit_file(interned(code));
main_menu(&mut prj)?; main_menu(&mut prj)?;
Ok(()) Ok(())
@@ -98,8 +94,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(unsafe { TREES.push(code) }); Populator::new(prj).visit_file(interned(code));
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
@@ -222,7 +218,7 @@ fn import_files(table: &mut Table) -> Result<(), RlError> {
} }
}; };
Populator::new(table).visit_file(unsafe { TREES.push(code) }); Populator::new(table).visit_file(interned(code));
println!("...Imported!"); println!("...Imported!");
Ok(Response::Accept) Ok(Response::Accept)
@@ -322,18 +318,11 @@ fn banner() {
); );
} }
/// Keeps leaked references to past ASTs, for posterity:tm: /// Interns a [File](cl_ast::File), returning a static reference to it.
struct TreeManager { fn interned(file: cl_ast::File) -> &'static cl_ast::File {
trees: Vec<&'static cl_ast::File>, use cl_structures::intern::{interned::Interned, typed_interner::TypedInterner};
} static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> =
LazyLock::new(Default::default);
impl TreeManager { Interned::to_ref(&INTERNER.get_or_insert(file))
const fn new() -> Self {
Self { trees: vec![] }
}
fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File {
let ptr = Box::leak(Box::new(tree));
self.trees.push(ptr);
ptr
}
} }

View File

@@ -20,7 +20,7 @@ pub enum Source<'a> {
Ty(&'a TyKind), Ty(&'a TyKind),
} }
impl<'a> Source<'a> { impl Source<'_> {
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<'a> Source<'a> {
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(l) => Some(l.name), Source::Local(_) => None,
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,7 +160,8 @@ 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<'a> Default for Table<'a> { impl Default for Table<'_> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }

View File

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

View File

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

View File

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

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

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

View File

@@ -1,5 +1,6 @@
//! Formats numbers in hexadecimal, octal, or binary //! Formats numbers in hexadecimal, octal, or binary
mod math; mod math;
use math::{min, count_leading_zeroes};
fn as_digit(n: u32) -> char { fn as_digit(n: u32) -> char {
(if n > 9 { (if n > 9 {

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

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

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

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

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

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

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

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