interpreter: BuiltIn
overhaul!
- Allowed builtins to self-describe - Broke builtins into their own module - Created a macro to work with BuiltIns easier - Uses macro 2.0 syntax, so it requires passing in ALL externally referenced identifiers - Luckily, this is already a requirement of good, readable macro definitions! - As a temporary hack, turn overloadable operators into function calls - This is kind of pointless at the moment, since there can only be one definition of a function per name (no ADL/function overloading/traits/type namespaces yet!) - This is also pretty slow, but benchmarking shows it's not as slow as I thought (~400x slower in release mode than a native Rust implementation when running `fib.cl`/`fib.rs`. Totally unacceptable for most work, but this is a tree walk interpreter.) - TODO: Remove this when desugaring from operators to function calls is implemented
This commit is contained in:
parent
e9dc8a7e32
commit
5eb6411d53
16
libconlang/examples/fib.rs
Normal file
16
libconlang/examples/fib.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Calculate Fibonacci numbers
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for num in 0..=30 {
|
||||||
|
println!("fib({num}) = {}", fib(num))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the classic recursive definition of fib()
|
||||||
|
fn fib(a: i64) -> i64 {
|
||||||
|
if a > 1 {
|
||||||
|
fib(a - 1) + fib(a - 2)
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,9 @@ pub trait Callable: std::fmt::Debug {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// [BuiltIn]s are [Callable]s with bespoke definitions
|
/// [BuiltIn]s are [Callable]s with bespoke definitions
|
||||||
pub trait BuiltIn: std::fmt::Debug + Callable {}
|
pub trait BuiltIn: std::fmt::Debug + Callable {
|
||||||
|
fn description(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod temp_type_impl {
|
pub mod temp_type_impl {
|
||||||
//! Temporary implementations of Conlang values
|
//! Temporary implementations of Conlang values
|
||||||
@ -164,6 +166,7 @@ pub mod temp_type_impl {
|
|||||||
String => ConValue::String,
|
String => ConValue::String,
|
||||||
Function => ConValue::Function,
|
Function => ConValue::Function,
|
||||||
Vec<ConValue> => ConValue::Tuple,
|
Vec<ConValue> => ConValue::Tuple,
|
||||||
|
&'static dyn BuiltIn => ConValue::BuiltIn,
|
||||||
}
|
}
|
||||||
impl From<()> for ConValue {
|
impl From<()> for ConValue {
|
||||||
fn from(_: ()) -> Self {
|
fn from(_: ()) -> Self {
|
||||||
@ -246,27 +249,6 @@ pub mod temp_type_impl {
|
|||||||
_ => Err(Error::TypeError)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
impl Neg for ConValue {
|
|
||||||
type Output = IResult<Self>;
|
|
||||||
fn neg(self) -> Self::Output {
|
|
||||||
Ok(match self {
|
|
||||||
ConValue::Empty => ConValue::Empty,
|
|
||||||
ConValue::Int(v) => ConValue::Int(-v),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Not for ConValue {
|
|
||||||
type Output = IResult<Self>;
|
|
||||||
fn not(self) -> Self::Output {
|
|
||||||
Ok(match self {
|
|
||||||
ConValue::Empty => ConValue::Empty,
|
|
||||||
ConValue::Int(v) => ConValue::Int(!v),
|
|
||||||
ConValue::Bool(v) => ConValue::Bool(!v),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for ConValue {
|
impl std::fmt::Display for ConValue {
|
||||||
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 {
|
||||||
@ -301,7 +283,7 @@ pub mod temp_type_impl {
|
|||||||
write!(f, "fn {}", func.name())
|
write!(f, "fn {}", func.name())
|
||||||
}
|
}
|
||||||
ConValue::BuiltIn(func) => {
|
ConValue::BuiltIn(func) => {
|
||||||
write!(f, "internal fn {}", func.name())
|
write!(f, "{}", func.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,48 +481,51 @@ pub mod interpret {
|
|||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
let Binary { head, tail } = self;
|
let Binary { head, tail } = self;
|
||||||
let mut head = head.interpret(env)?;
|
let mut head = head.interpret(env)?;
|
||||||
|
// Short-circuiting ops
|
||||||
for (op, tail) in tail {
|
for (op, tail) in tail {
|
||||||
head = match op {
|
match op {
|
||||||
BinaryKind::LogAnd => {
|
BinaryKind::LogAnd => {
|
||||||
if head.truthy()? {
|
if head.truthy()? {
|
||||||
tail.interpret(env)
|
head = tail.interpret(env)?;
|
||||||
} else {
|
continue;
|
||||||
return Ok(head); // Short circuiting
|
|
||||||
}
|
}
|
||||||
|
return Ok(head); // Short circuiting
|
||||||
}
|
}
|
||||||
BinaryKind::LogOr => {
|
BinaryKind::LogOr => {
|
||||||
if !head.truthy()? {
|
if !head.truthy()? {
|
||||||
tail.interpret(env)
|
head = tail.interpret(env)?;
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
return Ok(head); // Short circuiting
|
return Ok(head); // Short circuiting
|
||||||
}
|
}
|
||||||
}
|
|
||||||
BinaryKind::LogXor => {
|
BinaryKind::LogXor => {
|
||||||
// TODO: It should be possible to assemble better error information from
|
head = ConValue::Bool(head.truthy()? ^ tail.interpret(env)?.truthy()?);
|
||||||
// this
|
continue;
|
||||||
let (lhs, rhs) = (head.truthy()?, tail.interpret(env)?.truthy()?);
|
|
||||||
Ok(ConValue::Bool(lhs ^ rhs))
|
|
||||||
}
|
}
|
||||||
// TODO: For all overloadable operators, transmute into function call
|
_ => {}
|
||||||
BinaryKind::Mul => head * tail.interpret(env)?,
|
}
|
||||||
BinaryKind::Div => head / tail.interpret(env)?,
|
let tail = tail.interpret(env)?;
|
||||||
BinaryKind::Rem => head % tail.interpret(env)?,
|
head = match op {
|
||||||
BinaryKind::Add => head + tail.interpret(env)?,
|
BinaryKind::Mul => env.call("mul", &[head, tail]),
|
||||||
BinaryKind::Sub => head - tail.interpret(env)?,
|
BinaryKind::Div => env.call("div", &[head, tail]),
|
||||||
BinaryKind::Shl => head << tail.interpret(env)?,
|
BinaryKind::Rem => env.call("rem", &[head, tail]),
|
||||||
BinaryKind::Shr => head >> tail.interpret(env)?,
|
BinaryKind::Add => env.call("add", &[head, tail]),
|
||||||
BinaryKind::BitAnd => head & tail.interpret(env)?,
|
BinaryKind::Sub => env.call("sub", &[head, tail]),
|
||||||
BinaryKind::BitOr => head | tail.interpret(env)?,
|
BinaryKind::Shl => env.call("shl", &[head, tail]),
|
||||||
BinaryKind::BitXor => head ^ tail.interpret(env)?,
|
BinaryKind::Shr => env.call("shr", &[head, tail]),
|
||||||
BinaryKind::RangeExc => head.range_exc(tail.interpret(env)?),
|
BinaryKind::BitAnd => env.call("and", &[head, tail]),
|
||||||
BinaryKind::RangeInc => head.range_inc(tail.interpret(env)?),
|
BinaryKind::BitOr => env.call("or", &[head, tail]),
|
||||||
BinaryKind::Lt => head.lt(&tail.interpret(env)?),
|
BinaryKind::BitXor => env.call("xor", &[head, tail]),
|
||||||
BinaryKind::LtEq => head.lt_eq(&tail.interpret(env)?),
|
BinaryKind::RangeExc => env.call("range_exc", &[head, tail]),
|
||||||
BinaryKind::Equal => head.eq(&tail.interpret(env)?),
|
BinaryKind::RangeInc => env.call("range_inc", &[head, tail]),
|
||||||
BinaryKind::NotEq => head.neq(&tail.interpret(env)?),
|
BinaryKind::Lt => env.call("lt", &[head, tail]),
|
||||||
BinaryKind::GtEq => head.gt_eq(&tail.interpret(env)?),
|
BinaryKind::LtEq => env.call("lt_eq", &[head, tail]),
|
||||||
BinaryKind::Gt => head.gt(&tail.interpret(env)?),
|
BinaryKind::Equal => env.call("eq", &[head, tail]),
|
||||||
|
BinaryKind::NotEq => env.call("noteq", &[head, tail]),
|
||||||
|
BinaryKind::GtEq => env.call("gt_eq", &[head, tail]),
|
||||||
|
BinaryKind::Gt => env.call("gt", &[head, tail]),
|
||||||
BinaryKind::Dot => todo!("search within a type's namespace!"),
|
BinaryKind::Dot => todo!("search within a type's namespace!"),
|
||||||
|
_ => Ok(head),
|
||||||
}?;
|
}?;
|
||||||
}
|
}
|
||||||
Ok(head)
|
Ok(head)
|
||||||
@ -553,9 +538,9 @@ pub mod interpret {
|
|||||||
|
|
||||||
for op in ops.iter().rev() {
|
for op in ops.iter().rev() {
|
||||||
operand = match op {
|
operand = match op {
|
||||||
UnaryKind::Deref => todo!("Deref operator"),
|
UnaryKind::Deref => env.call("deref", &[operand])?,
|
||||||
UnaryKind::Neg => (-operand)?,
|
UnaryKind::Neg => env.call("neg", &[operand])?,
|
||||||
UnaryKind::Not => (!operand)?,
|
UnaryKind::Not => env.call("not", &[operand])?,
|
||||||
UnaryKind::At => {
|
UnaryKind::At => {
|
||||||
println!("{operand}");
|
println!("{operand}");
|
||||||
operand
|
operand
|
||||||
@ -646,7 +631,8 @@ pub mod interpret {
|
|||||||
}
|
}
|
||||||
impl Interpret for AddrOf {
|
impl Interpret for AddrOf {
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
||||||
todo!("Implement AddrOf in {env}")
|
let Self { count: _, mutable: _, expr } = self;
|
||||||
|
todo!("Create reference\nfrom expr: {expr}\nin env:\n{env}\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Interpret for Block {
|
impl Interpret for Block {
|
||||||
@ -809,77 +795,16 @@ pub mod function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builtin {
|
pub mod builtin;
|
||||||
//! Implementations of built-in functions
|
|
||||||
mod builtin_imports {
|
|
||||||
pub use crate::interpreter::{
|
|
||||||
env::Environment, error::IResult, temp_type_impl::ConValue, BuiltIn, Callable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
use super::BuiltIn;
|
|
||||||
/// Builtins to load when a new interpreter is created
|
|
||||||
pub const DEFAULT_BUILTINS: &[&dyn BuiltIn] = &[&print::Print, &dbg::Dbg, &dump::Dump];
|
|
||||||
|
|
||||||
mod print {
|
|
||||||
//! Implements the unstable `print(...)` builtin
|
|
||||||
use super::builtin_imports::*;
|
|
||||||
/// Implements the `print(...)` builtin
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Print;
|
|
||||||
impl BuiltIn for Print {}
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl Callable for Print {
|
|
||||||
fn name(&self) -> &'static str { "print" }
|
|
||||||
fn call(&self, _inter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
for arg in args {
|
|
||||||
print!("{arg}")
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod dbg {
|
|
||||||
//! Implements the unstable `dbg(...)` builtin
|
|
||||||
use super::builtin_imports::*;
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Dbg;
|
|
||||||
impl BuiltIn for Dbg {}
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl Callable for Dbg {
|
|
||||||
fn name(&self) -> &str { "dbg" }
|
|
||||||
fn call(&self, _inter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
println!("{args:?}");
|
|
||||||
Ok(args.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod dump {
|
|
||||||
use super::builtin_imports::*;
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Dump;
|
|
||||||
impl BuiltIn for Dump {}
|
|
||||||
impl Callable for Dump {
|
|
||||||
fn call(&self, env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
println!("{}", *env);
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dump"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod env {
|
pub mod env {
|
||||||
//! Lexical and non-lexical scoping for variables
|
//! Lexical and non-lexical scoping for variables
|
||||||
use super::{
|
use super::{
|
||||||
builtin::DEFAULT_BUILTINS,
|
builtin::{BINARY, MISC, RANGE, UNARY},
|
||||||
error::{Error, IResult},
|
error::{Error, IResult},
|
||||||
function::Function,
|
function::Function,
|
||||||
temp_type_impl::ConValue,
|
temp_type_impl::ConValue,
|
||||||
Callable, Interpret,
|
BuiltIn, Callable, Interpret,
|
||||||
};
|
};
|
||||||
use crate::ast::{Function as FnDecl, Identifier};
|
use crate::ast::{Function as FnDecl, Identifier};
|
||||||
use std::{
|
use std::{
|
||||||
@ -899,9 +824,9 @@ pub mod env {
|
|||||||
for (frame, name) in self.frames.iter().rev() {
|
for (frame, name) in self.frames.iter().rev() {
|
||||||
writeln!(f, "--- {name} ---")?;
|
writeln!(f, "--- {name} ---")?;
|
||||||
for (var, val) in frame {
|
for (var, val) in frame {
|
||||||
write!(f, "- {var}: ")?;
|
write!(f, "{var}: ")?;
|
||||||
match val {
|
match val {
|
||||||
Some(value) => writeln!(f, "{value}"),
|
Some(value) => writeln!(f, "\t{value}"),
|
||||||
None => writeln!(f, "<undefined>"),
|
None => writeln!(f, "<undefined>"),
|
||||||
}?
|
}?
|
||||||
}
|
}
|
||||||
@ -911,14 +836,22 @@ pub mod env {
|
|||||||
}
|
}
|
||||||
impl Default for Environment {
|
impl Default for Environment {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut builtins = HashMap::new();
|
Self {
|
||||||
for &builtin in DEFAULT_BUILTINS {
|
frames: vec![
|
||||||
builtins.insert(builtin.name().into(), Some(ConValue::BuiltIn(builtin)));
|
(to_hashmap(RANGE), "range ops"),
|
||||||
|
(to_hashmap(UNARY), "unary ops"),
|
||||||
|
(to_hashmap(BINARY), "binary ops"),
|
||||||
|
(to_hashmap(MISC), "builtins"),
|
||||||
|
(HashMap::new(), "globals"),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
// FIXME: Temporary until modules are implemented
|
|
||||||
Self { frames: vec![(builtins, "builtins"), (HashMap::new(), "globals")] }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<String, Option<ConValue>> {
|
||||||
|
from.iter()
|
||||||
|
.map(|&v| (v.name().into(), Some(v.into())))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -1073,7 +1006,11 @@ pub mod error {
|
|||||||
/// A value was called, but is not callable
|
/// A value was called, but is not callable
|
||||||
NotCallable(ConValue),
|
NotCallable(ConValue),
|
||||||
/// A function was called with the wrong number of arguments
|
/// A function was called with the wrong number of arguments
|
||||||
ArgNumber { want: usize, got: usize },
|
ArgNumber {
|
||||||
|
want: usize,
|
||||||
|
got: usize,
|
||||||
|
},
|
||||||
|
NullPointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
@ -1109,6 +1046,9 @@ pub mod error {
|
|||||||
Error::ArgNumber { want, got } => {
|
Error::ArgNumber { want, got } => {
|
||||||
write!(f, "Expected {want} arguments, got {got}")
|
write!(f, "Expected {want} arguments, got {got}")
|
||||||
}
|
}
|
||||||
|
Error::NullPointer => {
|
||||||
|
write!(f, "Attempted to dereference a null pointer?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
239
libconlang/src/interpreter/builtin.rs
Normal file
239
libconlang/src/interpreter/builtin.rs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
//! Implementations of built-in functions
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
env::Environment,
|
||||||
|
error::{Error, IResult},
|
||||||
|
temp_type_impl::ConValue,
|
||||||
|
BuiltIn, Callable,
|
||||||
|
};
|
||||||
|
use std::io::{stdout, Write};
|
||||||
|
|
||||||
|
builtins! {
|
||||||
|
const MISC;
|
||||||
|
/// Unstable variadic print function
|
||||||
|
pub fn print<_, args> () -> IResult<ConValue> {
|
||||||
|
let mut out = stdout().lock();
|
||||||
|
for arg in args {
|
||||||
|
write!(out, "{arg}").ok();
|
||||||
|
}
|
||||||
|
writeln!(out).ok();
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
/// Prints the [Debug](std::fmt::Debug) version of the input values
|
||||||
|
pub fn dbg<_, args> () -> IResult<ConValue> {
|
||||||
|
let mut out = stdout().lock();
|
||||||
|
for arg in args {
|
||||||
|
writeln!(out, "{arg:?}").ok();
|
||||||
|
}
|
||||||
|
Ok(args.into())
|
||||||
|
}
|
||||||
|
/// Dumps info from the environment
|
||||||
|
pub fn dump<env, _>() -> IResult<ConValue> {
|
||||||
|
println!("{}", *env);
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builtins! {
|
||||||
|
const BINARY;
|
||||||
|
/// Multiplication `a * b`
|
||||||
|
pub fn mul(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
|
||||||
|
_ => Err(Error::TypeError)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Division `a / b`
|
||||||
|
pub fn div(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs){
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
|
||||||
|
_ => Err(Error::TypeError)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Remainder `a % b`
|
||||||
|
pub fn rem(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Addition `a + b`
|
||||||
|
pub fn add(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
|
||||||
|
(ConValue::String(a), ConValue::String(b)) => ConValue::String(a.to_string() + b),
|
||||||
|
_ => Err(Error::TypeError)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Subtraction `a - b`
|
||||||
|
pub fn sub(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shift Left `a << b`
|
||||||
|
pub fn shl(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Shift Right `a >> b`
|
||||||
|
pub fn shr(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bitwise And `a & b`
|
||||||
|
pub fn and(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Bitwise Or `a | b`
|
||||||
|
pub fn or(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Bitwise Exclusive Or `a ^ b`
|
||||||
|
pub fn xor(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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`
|
||||||
|
pub fn range_exc(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
||||||
|
Err(Error::TypeError)?
|
||||||
|
};
|
||||||
|
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1)))
|
||||||
|
}
|
||||||
|
/// Inclusive Range `a..=b`
|
||||||
|
pub fn range_inc(lhs, rhs) -> IResult<ConValue> {
|
||||||
|
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
||||||
|
Err(Error::TypeError)?
|
||||||
|
};
|
||||||
|
Ok(ConValue::RangeInc(lhs, rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builtins! {
|
||||||
|
const UNARY;
|
||||||
|
/// Negates the ConValue
|
||||||
|
pub fn neg(tail) -> IResult<ConValue> {
|
||||||
|
Ok(match tail {
|
||||||
|
ConValue::Empty => ConValue::Empty,
|
||||||
|
ConValue::Int(v) => ConValue::Int(-v),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Inverts the ConValue
|
||||||
|
pub fn not(tail) -> IResult<ConValue> {
|
||||||
|
Ok(match tail {
|
||||||
|
ConValue::Empty => ConValue::Empty,
|
||||||
|
ConValue::Int(v) => ConValue::Int(!v),
|
||||||
|
ConValue::Bool(v) => ConValue::Bool(!v),
|
||||||
|
_ => Err(Error::TypeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> &str { stringify!($name) }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Templates comparison functions for [ConValue]
|
||||||
|
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
|
||||||
|
match ($a, $b) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
|
||||||
|
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
|
||||||
|
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(a $op b)),
|
||||||
|
_ => Err(Error::TypeError)
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
// Calculate Fibonacci numbers
|
// Calculate Fibonacci numbers
|
||||||
|
|
||||||
fn main() -> i128 {
|
fn main() {
|
||||||
let num = 10;
|
for num in 0..=30 {
|
||||||
print("fib(", num, "): ", fib(num));
|
print("fib(", num, ") = ", fib(num))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements the classic recursive definition of fib()
|
/// Implements the classic recursive definition of fib()
|
||||||
fn fib(a: i128) -> i128 {
|
fn fib(a: i64) -> i64 {
|
||||||
if a > 1 {
|
if a > 1 {
|
||||||
fib(a - 1) + fib(a - 2)
|
fib(a - 1) + fib(a - 2)
|
||||||
} else {
|
} else {
|
Loading…
Reference in New Issue
Block a user