mirror of https://github.com/vinc/moros.git
Add BigInt support to Lisp (#415)
* Add bigint support to Lisp * Remove Box * Replace BigInt by i64 * Add back big int * Work around big int errors * Print floats with a dot * Add conversion to f64 * Use Number#parse instead of double * Add trigo functions to Number * Add conversion from number to byte * Add addition to Number * Add multiplication to Number * Add negation and substraction to Number * Add division to Number * Add reminder to Number * Add pow to Number * Fix tests * Re-enable BigInt * Add parsing and printing of BigInt * Add sign * Add operations on BigInt * Fix compilation issues * Add support for add and mul overflow * Fix bigint conversion to and from str * Add number-type function * Add tests * Add support for pow overflow * Fix tests * Add more checks for overflow * Add check for division by zero * Fix typo * Return inf for large exponential operations * Check for division by zero in modulo * Add shift operations * Rewrite comparisons * Add lazy eval to cond expressions * Add set fonction * Add loop function * Add pi example * Add builtin pi example to shell * Update allocation error messages * Rewrite number conversions * Remove debug output from pi example * Move pi command to a dedicated file * Rewrite bytes->number and number->bytes * Update doc * Move op impl to Number * Add macros to dry code * Add more macros * Run clippy
This commit is contained in:
parent
cbd8d9b479
commit
892b697bd3
|
@ -131,15 +131,6 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "1.2.0"
|
||||
|
@ -253,13 +244,14 @@ dependencies = [
|
|||
"base64",
|
||||
"bit_field",
|
||||
"bootloader",
|
||||
"float-cmp",
|
||||
"hmac",
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"linked_list_allocator",
|
||||
"littlewing",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"object",
|
||||
"pbkdf2",
|
||||
"pc-keyboard",
|
||||
|
@ -292,6 +284,27 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
|
|
|
@ -20,13 +20,14 @@ aml = "0.16.1"
|
|||
base64 = { version = "0.13.0", default-features = false }
|
||||
bit_field = "0.10.0"
|
||||
bootloader = { version = "0.9.23", features = ["map_physical_memory"] }
|
||||
float-cmp = "0.9.0"
|
||||
hmac = { version = "0.12.1", default-features = false }
|
||||
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||
libm = "0.2.5"
|
||||
linked_list_allocator = "0.10.4"
|
||||
littlewing = { version = "0.7.0", default-features = false }
|
||||
nom = { version = "7.1.1", default-features = false, features = ["alloc"] }
|
||||
num-bigint = { version = "0.4.3", default-features = false }
|
||||
num-traits = { version = "0.2.15", default-features = false }
|
||||
object = { version = "0.29.0", default-features = false, features = ["read"] }
|
||||
pbkdf2 = { version = "0.11.0", default-features = false }
|
||||
pc-keyboard = "0.5.1"
|
||||
|
|
|
@ -14,6 +14,11 @@ In version 0.2.0 the whole implementation was refactored and the parser was
|
|||
rewritten to use [Nom](https://github.com/Geal/nom). This allowed the addition
|
||||
of strings to the language and reading from the filesystem.
|
||||
|
||||
|
||||
## Types
|
||||
- Basics: `bool`, `list`, `symbol`, `string`
|
||||
- Numbers: `float`, `int`, `bigint`
|
||||
|
||||
## Seven Primitive Operators
|
||||
- `quote` (with the `'` syntax)
|
||||
- `atom` (aliased to `atom?`)
|
||||
|
|
|
@ -97,10 +97,10 @@
|
|||
(print "\n")))
|
||||
|
||||
(define (uptime)
|
||||
(bytes->number (read-file-bytes "/dev/clk/uptime" 8)))
|
||||
(bytes->number (read-file-bytes "/dev/clk/uptime" 8) "float"))
|
||||
|
||||
(define (realtime)
|
||||
(bytes->number (read-file-bytes "realtime" 8)))
|
||||
(bytes->number (read-file-bytes "realtime" 8) "float"))
|
||||
|
||||
(define (write-file path str)
|
||||
(write-file-bytes path (string->bytes str)))
|
||||
|
|
|
@ -1,14 +1,36 @@
|
|||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(define (pi-nth n)
|
||||
(* (^ 16 (- n)) (-
|
||||
(/ 4 (+ 1 (* 8 n)))
|
||||
(/ 2 (+ 4 (* 8 n)))
|
||||
(/ 1 (+ 5 (* 8 n)))
|
||||
(/ 1 (+ 6 (* 8 n))))))
|
||||
|
||||
(define (pi-digits n)
|
||||
(apply + (map pi-nth (range 0 n))))
|
||||
(define (pi-digits y)
|
||||
(do
|
||||
(define dot true)
|
||||
(define q 1)
|
||||
(define r 0)
|
||||
(define t 1)
|
||||
(define k 1)
|
||||
(define n 3)
|
||||
(define l 3)
|
||||
(map
|
||||
(lambda (j)
|
||||
(do
|
||||
(cond
|
||||
((< (- (+ (* q 4) r) t) (* n t)) (do
|
||||
(print (string n (cond (dot ".") (true ""))))
|
||||
(set dot false)
|
||||
(define nr (* 10 (- r (* n t))))
|
||||
(set n (- (/ (* 10 (+ (* 3 q) r)) t) (* 10 n)))
|
||||
(set q (* q 10))
|
||||
(set r nr)))
|
||||
(true (do
|
||||
(define nr (* (+ (* 2 q) r) l))
|
||||
(define nn (/ (+ 2 (* q k 7) (* r l)) (* t l)))
|
||||
(set q (* q k))
|
||||
(set t (* t l))
|
||||
(set l (+ l 2))
|
||||
(set k (+ k 1))
|
||||
(set n nn)
|
||||
(set r nr))))))
|
||||
(range 0 y))
|
||||
n))
|
||||
|
||||
(println
|
||||
(cond
|
||||
|
|
|
@ -43,7 +43,10 @@ pub fn init(boot_info: &'static BootInfo) {
|
|||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
panic!("allocation error: {:?}", layout)
|
||||
let csi_color = api::console::Style::color("LightRed");
|
||||
let csi_reset = api::console::Style::reset();
|
||||
printk!("{}Error:{} Could not allocate {} bytes\n", csi_color, csi_reset, layout.size());
|
||||
hlt_loop();
|
||||
}
|
||||
|
||||
pub trait Testable {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::api;
|
||||
use crate::{api, sys, hlt_loop};
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys;
|
||||
use crate::sys::process::Registers;
|
||||
|
||||
use core::arch::asm;
|
||||
|
@ -112,13 +111,13 @@ extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame,
|
|||
//debug!("EXCEPTION: PAGE FAULT ({:?})", error_code);
|
||||
let addr = Cr2::read().as_u64();
|
||||
if sys::allocator::alloc_pages(addr, 1).is_err() {
|
||||
let csi_color = api::console::Style::color("LightRed");
|
||||
let csi_reset = api::console::Style::reset();
|
||||
printk!("{}Error:{} Could not allocate address {:#x}\n", csi_color, csi_reset, addr);
|
||||
if error_code.contains(PageFaultErrorCode::USER_MODE) {
|
||||
let csi_color = api::console::Style::color("LightRed");
|
||||
let csi_reset = api::console::Style::reset();
|
||||
printk!("{}Error:{} Could not allocate address {:#x}\n", csi_color, csi_reset, addr);
|
||||
api::syscall::exit(ExitCode::PageFaultError);
|
||||
} else {
|
||||
panic!();
|
||||
hlt_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
605
src/usr/lisp.rs
605
src/usr/lisp.rs
|
@ -15,10 +15,15 @@ use alloc::vec;
|
|||
use core::borrow::Borrow;
|
||||
use core::cell::RefCell;
|
||||
use core::convert::TryInto;
|
||||
use core::convert::TryFrom;
|
||||
use core::f64::consts::PI;
|
||||
use core::fmt;
|
||||
use float_cmp::approx_eq;
|
||||
use core::ops::{Neg, Add, Div, Mul, Sub, Rem, Shl, Shr};
|
||||
use core::str::FromStr;
|
||||
use lazy_static::lazy_static;
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use num_traits::Zero;
|
||||
use spin::Mutex;
|
||||
|
||||
use nom::IResult;
|
||||
|
@ -29,13 +34,15 @@ use nom::bytes::complete::tag;
|
|||
use nom::bytes::complete::take_while1;
|
||||
use nom::character::complete::char;
|
||||
use nom::character::complete::multispace0;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::value;
|
||||
use nom::multi::many0;
|
||||
use nom::number::complete::double;
|
||||
use nom::sequence::delimited;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
use nom::combinator::recognize;
|
||||
|
||||
// Eval & Env adapted from Risp
|
||||
// Copyright 2019 Stepan Parunashvili
|
||||
|
@ -52,13 +59,320 @@ use nom::sequence::preceded;
|
|||
|
||||
// Types
|
||||
|
||||
#[derive(Clone, PartialEq, PartialOrd)]
|
||||
pub enum Number {
|
||||
BigInt(BigInt),
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
}
|
||||
|
||||
macro_rules! trigonometric_method {
|
||||
($op:ident) => {
|
||||
fn $op(&self) -> Number {
|
||||
Number::Float(libm::$op(self.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! arithmetic_method {
|
||||
($op:ident, $checked_op:ident) => {
|
||||
fn $op(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.$checked_op(b) {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(BigInt::from(a).$op(BigInt::from(b)))
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float((a as f64).$op(b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(a.$op(b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(a.$op(b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
trigonometric_method!(cos);
|
||||
trigonometric_method!(sin);
|
||||
trigonometric_method!(tan);
|
||||
trigonometric_method!(acos);
|
||||
trigonometric_method!(asin);
|
||||
trigonometric_method!(atan);
|
||||
|
||||
arithmetic_method!(add, checked_add);
|
||||
arithmetic_method!(sub, checked_sub);
|
||||
arithmetic_method!(mul, checked_mul);
|
||||
arithmetic_method!(div, checked_div);
|
||||
|
||||
// NOTE: Rem use `libm::fmod` for `f64` instead of `rem`
|
||||
fn rem(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_rem(b) {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(BigInt::from(a).rem(BigInt::from(b)))
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float(libm::fmod(a as f64, b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(libm::fmod(a, b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(libm::fmod(a, b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn pow(&self, other: &Number) -> Number {
|
||||
let bmax = BigInt::from(u32::MAX);
|
||||
let imax = u32::MAX as i64;
|
||||
match (self, other) {
|
||||
(_, Number::BigInt(b)) if *b > bmax => Number::Float(f64::INFINITY),
|
||||
(_, Number::Int(b)) if *b > imax => Number::Float(f64::INFINITY),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.pow(*b as u32)),
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_pow(*b as u32) {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(BigInt::from(*a)).pow(other)
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float(libm::pow(*a as f64, *b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(libm::pow(*a, *b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(libm::pow(*a, *b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn neg(self) -> Number {
|
||||
match self {
|
||||
Number::BigInt(a) => Number::BigInt(-a),
|
||||
Number::Int(a) => {
|
||||
if let Some(r) = a.checked_neg() {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(-BigInt::from(a))
|
||||
}
|
||||
}
|
||||
Number::Float(a) => Number::Float(-a),
|
||||
}
|
||||
}
|
||||
|
||||
fn shl(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shl(b)),
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_shl(b as u32) {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(BigInt::from(a).shl(b))
|
||||
}
|
||||
}
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn shr(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shr(b)),
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_shr(b as u32) {
|
||||
Number::Int(r)
|
||||
} else {
|
||||
Number::BigInt(BigInt::from(a).shr(b))
|
||||
}
|
||||
}
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn to_be_bytes(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Number::Int(n) => n.to_be_bytes().to_vec(),
|
||||
Number::Float(n) => n.to_be_bytes().to_vec(),
|
||||
Number::BigInt(n) => n.to_bytes_be().1, // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
Number::Int(n) => *n == 0,
|
||||
Number::Float(n) => *n == 0.0,
|
||||
Number::BigInt(n) => n.is_zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Number {
|
||||
type Output = Number;
|
||||
fn neg(self) -> Number {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! operator {
|
||||
($t:ty, $op:ident) => {
|
||||
impl $t for Number {
|
||||
type Output = Number;
|
||||
fn $op(self, other: Number) -> Number {
|
||||
self.$op(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator!(Add, add);
|
||||
operator!(Sub, sub);
|
||||
operator!(Mul, mul);
|
||||
operator!(Div, div);
|
||||
operator!(Rem, rem);
|
||||
operator!(Shl, shl);
|
||||
operator!(Shr, shr);
|
||||
|
||||
impl FromStr for Number {
|
||||
type Err = Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.contains('.') {
|
||||
if let Ok(n) = s.parse() {
|
||||
return Ok(Number::Float(n));
|
||||
}
|
||||
} else if let Ok(n) = s.parse() {
|
||||
return Ok(Number::Int(n));
|
||||
} else {
|
||||
let mut chars = s.chars().peekable();
|
||||
let is_neg = chars.peek() == Some(&'-');
|
||||
if is_neg {
|
||||
chars.next().unwrap();
|
||||
}
|
||||
let mut res = BigInt::from(0);
|
||||
for c in chars {
|
||||
let d = c as u8 - b'0';
|
||||
res = res * BigInt::from(10) + BigInt::from(d as u32);
|
||||
}
|
||||
res *= BigInt::from(if is_neg { -1 } else { 1 });
|
||||
return Ok(Number::BigInt(res));
|
||||
} /* else if let Ok(n) = s.parse() { // FIXME: rust-lld: error: undefined symbol: fmod
|
||||
return Ok(Number::BigInt(n));
|
||||
} */
|
||||
Err(Err::Reason("Could not parse number".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Number {
|
||||
fn from(s: &str) -> Self {
|
||||
if let Ok(num) = s.parse() {
|
||||
num
|
||||
} else {
|
||||
Number::Float(f64::NAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
fn from(num: f64) -> Self {
|
||||
Number::Float(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Number {
|
||||
fn from(num: u8) -> Self {
|
||||
Number::Int(num as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Number {
|
||||
fn from(num: usize) -> Self {
|
||||
if num > i64::MAX as usize {
|
||||
Number::BigInt(BigInt::from(num))
|
||||
} else {
|
||||
Number::Int(num as i64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Number> for f64 {
|
||||
fn from(num: &Number) -> f64 {
|
||||
match num {
|
||||
Number::Float(n) => *n,
|
||||
Number::Int(n) => *n as f64,
|
||||
Number::BigInt(n) => n.to_f64().unwrap_or(f64::NAN),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_from_number {
|
||||
($int:ident, $to_int:ident) => {
|
||||
impl TryFrom<Number> for $int {
|
||||
type Error = Err;
|
||||
|
||||
fn try_from(num: Number) -> Result<Self, Self::Error> {
|
||||
let err = Err::Reason(format!("Expected an integer between 0 and {}", $int::MAX));
|
||||
match num {
|
||||
Number::Float(n) => $int::try_from(n as i64).or(Err(err)),
|
||||
Number::Int(n) => $int::try_from(n).or(Err(err)),
|
||||
Number::BigInt(n) => n.$to_int().ok_or(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try_from_number!(usize, to_usize);
|
||||
try_from_number!(u8, to_u8);
|
||||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
//write!(f, "{}", self), // FIXME: alloc error
|
||||
match self {
|
||||
Number::Int(n) => {
|
||||
write!(f, "{}", n)
|
||||
}
|
||||
Number::BigInt(n) => {
|
||||
//write!(f, "{}", n), // FIXME: rust-lld: error: undefined symbol: fmod
|
||||
let mut v = Vec::new();
|
||||
let mut n = n.clone();
|
||||
if n < BigInt::from(0) {
|
||||
write!(f, "-").ok();
|
||||
n = -n;
|
||||
}
|
||||
loop {
|
||||
v.push((n.clone() % BigInt::from(10)).to_u64().unwrap());
|
||||
n = n / BigInt::from(10);
|
||||
if n == BigInt::from(0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for d in v.iter().rev() {
|
||||
write!(f, "{}", d).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Number::Float(n) => {
|
||||
if n - libm::trunc(*n) == 0.0 {
|
||||
write!(f, "{}.0", n)
|
||||
} else {
|
||||
write!(f, "{}", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Exp {
|
||||
Primitive(fn(&[Exp]) -> Result<Exp, Err>),
|
||||
Lambda(Lambda),
|
||||
List(Vec<Exp>),
|
||||
Bool(bool),
|
||||
Num(f64),
|
||||
Num(Number),
|
||||
Str(String),
|
||||
Sym(String),
|
||||
}
|
||||
|
@ -102,7 +416,7 @@ struct Lambda {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Err {
|
||||
pub enum Err {
|
||||
Reason(String),
|
||||
}
|
||||
|
||||
|
@ -153,8 +467,12 @@ fn parse_sym(input: &str) -> IResult<&str, Exp> {
|
|||
}
|
||||
|
||||
fn parse_num(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, num) = double(input)?;
|
||||
Ok((input, Exp::Num(num)))
|
||||
let (input, num) = recognize(tuple((
|
||||
opt(alt((char('+'), char('-')))),
|
||||
digit1,
|
||||
opt(tuple((char('.'), digit1)))
|
||||
)))(input)?;
|
||||
Ok((input, Exp::Num(Number::from(num))))
|
||||
}
|
||||
|
||||
fn parse_bool(input: &str) -> IResult<&str, Exp> {
|
||||
|
@ -186,24 +504,6 @@ fn parse(input: &str)-> Result<(String, Exp), Err> {
|
|||
|
||||
// Env
|
||||
|
||||
macro_rules! ensure_tonicity {
|
||||
($check_fn:expr) => {
|
||||
|args: &[Exp]| -> Result<Exp, Err> {
|
||||
let floats = list_of_floats(args)?;
|
||||
ensure_length_gt!(floats, 0);
|
||||
let first = &floats[0];
|
||||
let rest = &floats[1..];
|
||||
fn f(prev: &f64, xs: &[f64]) -> bool {
|
||||
match xs.first() {
|
||||
Some(x) => $check_fn(*prev, *x) && f(x, &xs[1..]),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
Ok(Exp::Bool(f(first, rest)))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ensure_length_eq {
|
||||
($list:expr, $count:expr) => {
|
||||
if $list.len() != $count {
|
||||
|
@ -224,100 +524,125 @@ macro_rules! ensure_length_gt {
|
|||
|
||||
fn default_env() -> Rc<RefCell<Env>> {
|
||||
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
|
||||
data.insert("pi".to_string(), Exp::Num(PI));
|
||||
data.insert("=".to_string(), Exp::Primitive(ensure_tonicity!(|a, b| approx_eq!(f64, a, b))));
|
||||
data.insert(">".to_string(), Exp::Primitive(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a > b)));
|
||||
data.insert(">=".to_string(), Exp::Primitive(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a > b)));
|
||||
data.insert("<".to_string(), Exp::Primitive(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a < b)));
|
||||
data.insert("<=".to_string(), Exp::Primitive(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a < b)));
|
||||
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
|
||||
data.insert("=".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])))
|
||||
}));
|
||||
data.insert(">".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])))
|
||||
}));
|
||||
data.insert(">=".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])))
|
||||
}));
|
||||
data.insert("<".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])))
|
||||
}));
|
||||
data.insert("<=".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])))
|
||||
}));
|
||||
data.insert("*".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
let res = list_of_floats(args)?.iter().fold(1.0, |acc, a| acc * a);
|
||||
let res = list_of_numbers(args)?.iter().fold(Number::Int(1), |acc, a| acc * a.clone());
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("+".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
let res = list_of_floats(args)?.iter().fold(0.0, |acc, a| acc + a);
|
||||
let res = list_of_numbers(args)?.iter().fold(Number::Int(0), |acc, a| acc + a.clone());
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("-".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 0);
|
||||
let args = list_of_floats(args)?;
|
||||
let car = args[0];
|
||||
let args = list_of_numbers(args)?;
|
||||
let car = args[0].clone();
|
||||
if args.len() == 1 {
|
||||
Ok(Exp::Num(-car))
|
||||
} else {
|
||||
let res = args[1..].iter().fold(0.0, |acc, a| acc + a);
|
||||
let res = args[1..].iter().fold(Number::Int(0), |acc, a| acc + a.clone());
|
||||
Ok(Exp::Num(car - res))
|
||||
}
|
||||
}));
|
||||
data.insert("/".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 0);
|
||||
let args = list_of_floats(args)?;
|
||||
let car = args[0];
|
||||
let mut args = list_of_numbers(args)?;
|
||||
if args.len() == 1 {
|
||||
Ok(Exp::Num(1.0 / car))
|
||||
} else {
|
||||
let res = args[1..].iter().fold(car, |acc, a| acc / a);
|
||||
Ok(Exp::Num(res))
|
||||
args.insert(0, Number::Int(1));
|
||||
}
|
||||
for arg in &args[1..] {
|
||||
if arg.is_zero() {
|
||||
return Err(Err::Reason("Division by zero".to_string()));
|
||||
}
|
||||
}
|
||||
let car = args[0].clone();
|
||||
let res = args[1..].iter().fold(car, |acc, a| acc / a.clone());
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("%".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 0);
|
||||
let args = list_of_floats(args)?;
|
||||
let car = args[0];
|
||||
let res = args[1..].iter().fold(car, |acc, a| libm::fmod(acc, *a));
|
||||
let args = list_of_numbers(args)?;
|
||||
for arg in &args[1..] {
|
||||
if arg.is_zero() {
|
||||
return Err(Err::Reason("Division by zero".to_string()));
|
||||
}
|
||||
}
|
||||
let car = args[0].clone();
|
||||
let res = args[1..].iter().fold(car, |acc, a| acc % a.clone());
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("^".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 0);
|
||||
let args = list_of_floats(args)?;
|
||||
let car = args[0];
|
||||
let res = args[1..].iter().fold(car, |acc, a| libm::pow(acc, *a));
|
||||
let args = list_of_numbers(args)?;
|
||||
let car = args[0].clone();
|
||||
let res = args[1..].iter().fold(car, |acc, a| acc.pow(a));
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("<<".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let args = list_of_numbers(args)?;
|
||||
let res = args[0].clone() << args[1].clone();
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert(">>".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let args = list_of_numbers(args)?;
|
||||
let res = args[0].clone() >> args[1].clone();
|
||||
Ok(Exp::Num(res))
|
||||
}));
|
||||
data.insert("cos".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
Ok(Exp::Num(libm::cos(args[0])))
|
||||
Ok(Exp::Num(number(&args[0])?.cos()))
|
||||
}));
|
||||
data.insert("acos".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
if -1.0 <= args[0] && args[0] <= 1.0 {
|
||||
Ok(Exp::Num(libm::acos(args[0])))
|
||||
if -1.0 <= float(&args[0])? && float(&args[0])? <= 1.0 {
|
||||
Ok(Exp::Num(number(&args[0])?.acos()))
|
||||
} else {
|
||||
Err(Err::Reason("Expected arg to be between -1.0 and 1.0".to_string()))
|
||||
}
|
||||
}));
|
||||
data.insert("asin".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
if -1.0 <= args[0] && args[0] <= 1.0 {
|
||||
Ok(Exp::Num(libm::asin(args[0])))
|
||||
if -1.0 <= float(&args[0])? && float(&args[0])? <= 1.0 {
|
||||
Ok(Exp::Num(number(&args[0])?.asin()))
|
||||
} else {
|
||||
Err(Err::Reason("Expected arg to be between -1.0 and 1.0".to_string()))
|
||||
}
|
||||
}));
|
||||
data.insert("atan".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
Ok(Exp::Num(libm::atan(args[0])))
|
||||
Ok(Exp::Num(number(&args[0])?.atan()))
|
||||
}));
|
||||
data.insert("sin".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
Ok(Exp::Num(libm::sin(args[0])))
|
||||
Ok(Exp::Num(number(&args[0])?.sin()))
|
||||
}));
|
||||
data.insert("tan".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let args = list_of_floats(args)?;
|
||||
Ok(Exp::Num(libm::tan(args[0])))
|
||||
Ok(Exp::Num(number(&args[0])?.tan()))
|
||||
}));
|
||||
data.insert("system".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let cmd = string(&args[0])?;
|
||||
match usr::shell::exec(&cmd) {
|
||||
Ok(()) => Ok(Exp::Num(0.0)),
|
||||
Err(code) => Ok(Exp::Num(code as u8 as f64)),
|
||||
Ok(()) => Ok(Exp::Num(Number::from(0 as u8))),
|
||||
Err(code) => Ok(Exp::Num(Number::from(code as u8))),
|
||||
}
|
||||
}));
|
||||
data.insert("read-file".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
|
@ -329,11 +654,11 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
data.insert("read-file-bytes".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let path = string(&args[0])?;
|
||||
let len = float(&args[1])?;
|
||||
let mut buf = vec![0; len as usize];
|
||||
let len = number(&args[1])?;
|
||||
let mut buf = vec![0; len.try_into()?];
|
||||
let bytes = fs::read(&path, &mut buf).or(Err(Err::Reason("Could not read file".to_string())))?;
|
||||
buf.resize(bytes, 0);
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(*b as f64)).collect()))
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}));
|
||||
data.insert("write-file-bytes".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
|
@ -342,7 +667,7 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
Exp::List(list) => {
|
||||
let buf = list_of_bytes(list)?;
|
||||
let bytes = fs::write(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
|
||||
Ok(Exp::Num(bytes as f64))
|
||||
Ok(Exp::Num(Number::from(bytes)))
|
||||
}
|
||||
_ => Err(Err::Reason("Expected second arg to be a list".to_string()))
|
||||
}
|
||||
|
@ -354,7 +679,7 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
Exp::List(list) => {
|
||||
let buf = list_of_bytes(list)?;
|
||||
let bytes = fs::append(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
|
||||
Ok(Exp::Num(bytes as f64))
|
||||
Ok(Exp::Num(Number::from(bytes)))
|
||||
}
|
||||
_ => Err(Err::Reason("Expected second arg to be a list".to_string()))
|
||||
}
|
||||
|
@ -370,7 +695,7 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
ensure_length_eq!(args, 1);
|
||||
let s = string(&args[0])?;
|
||||
let buf = s.as_bytes();
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(*b as f64)).collect()))
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}));
|
||||
data.insert("bytes->string".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
|
@ -384,27 +709,32 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
}
|
||||
}));
|
||||
data.insert("bytes->number".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match &args[0] {
|
||||
Exp::List(list) => {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) { // TODO: default type to "int" and make it optional
|
||||
(Exp::List(list), Exp::Str(kind)) => {
|
||||
let bytes = list_of_bytes(list)?;
|
||||
ensure_length_eq!(bytes, 8);
|
||||
Ok(Exp::Num(f64::from_be_bytes(bytes[0..8].try_into().unwrap())))
|
||||
match kind.as_str() { // TODO: bigint
|
||||
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(bytes[0..8].try_into().unwrap())))),
|
||||
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(bytes[0..8].try_into().unwrap())))),
|
||||
_ => Err(Err::Reason("Invalid number type".to_string())),
|
||||
}
|
||||
}
|
||||
_ => Err(Err::Reason("Expected arg to be a list".to_string()))
|
||||
_ => Err(Err::Reason("Expected args to be the number type and a list of bytes".to_string()))
|
||||
}
|
||||
|
||||
}));
|
||||
data.insert("number->bytes".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let f = float(&args[0])?;
|
||||
Ok(Exp::List(f.to_be_bytes().iter().map(|b| Exp::Num(*b as f64)).collect()))
|
||||
let n = number(&args[0])?;
|
||||
Ok(Exp::List(n.to_be_bytes().iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}));
|
||||
data.insert("regex-find".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) {
|
||||
(Exp::Str(regex), Exp::Str(s)) => {
|
||||
let res = Regex::new(regex).find(s).map(|(a, b)| {
|
||||
vec![Exp::Num(a as f64), Exp::Num(b as f64)]
|
||||
vec![Exp::Num(Number::from(a)), Exp::Num(Number::from(b))]
|
||||
}).unwrap_or(vec![]);
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
|
@ -436,6 +766,15 @@ fn default_env() -> Rc<RefCell<Env>> {
|
|||
};
|
||||
Ok(Exp::Str(exp.to_string()))
|
||||
}));
|
||||
data.insert("number-type".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match args[0] {
|
||||
Exp::Num(Number::Int(_)) => Ok(Exp::Str("int".to_string())),
|
||||
Exp::Num(Number::BigInt(_)) => Ok(Exp::Str("bigint".to_string())),
|
||||
Exp::Num(Number::Float(_)) => Ok(Exp::Str("float".to_string())),
|
||||
_ => Err(Err::Reason("Expected arg to be a number".to_string()))
|
||||
}
|
||||
}));
|
||||
data.insert("list".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
|
||||
Ok(Exp::List(args.to_vec()))
|
||||
}));
|
||||
|
@ -466,8 +805,8 @@ fn list_of_symbols(form: &Exp) -> Result<Vec<String>, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
fn list_of_floats(args: &[Exp]) -> Result<Vec<f64>, Err> {
|
||||
args.iter().map(float).collect()
|
||||
fn list_of_numbers(args: &[Exp]) -> Result<Vec<Number>, Err> {
|
||||
args.iter().map(number).collect()
|
||||
}
|
||||
|
||||
fn list_of_bytes(args: &[Exp]) -> Result<Vec<u8>, Err> {
|
||||
|
@ -481,22 +820,24 @@ fn string(exp: &Exp) -> Result<String, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
fn float(exp: &Exp) -> Result<f64, Err> {
|
||||
fn number(exp: &Exp) -> Result<Number, Err> {
|
||||
match exp {
|
||||
Exp::Num(num) => Ok(*num),
|
||||
Exp::Num(num) => Ok(num.clone()),
|
||||
_ => Err(Err::Reason("Expected a number".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn byte(exp: &Exp) -> Result<u8, Err> {
|
||||
let num = float(exp)?;
|
||||
if num >= 0.0 && num < u8::MAX.into() && (num - libm::trunc(num) == 0.0) {
|
||||
Ok(num as u8)
|
||||
} else {
|
||||
Err(Err::Reason(format!("Expected an integer between 0 and {}", u8::MAX)))
|
||||
fn float(exp: &Exp) -> Result<f64, Err> {
|
||||
match exp {
|
||||
Exp::Num(num) => Ok(num.into()),
|
||||
_ => Err(Err::Reason("Expected a float".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn byte(exp: &Exp) -> Result<u8, Err> {
|
||||
number(exp)?.try_into()
|
||||
}
|
||||
|
||||
// Eval
|
||||
|
||||
fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
|
||||
|
@ -558,10 +899,8 @@ fn eval_cond_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
match arg {
|
||||
Exp::List(list) => {
|
||||
ensure_length_eq!(list, 2);
|
||||
let pred = eval(&list[0], env)?;
|
||||
let exp = eval(&list[1], env)?;
|
||||
match pred {
|
||||
Exp::Bool(b) if b => return Ok(exp),
|
||||
match eval(&list[0], env)? {
|
||||
Exp::Bool(b) if b => return eval(&list[1], env),
|
||||
_ => continue,
|
||||
}
|
||||
},
|
||||
|
@ -593,6 +932,25 @@ fn eval_label_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_set_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match &args[0] {
|
||||
Exp::Sym(name) => {
|
||||
let exp = eval(&args[1], env)?;
|
||||
env_set(name, exp, env)?;
|
||||
Ok(Exp::Sym(name.clone()))
|
||||
}
|
||||
_ => Err(Err::Reason("Expected first argument to be a symbol".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_loop_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
loop {
|
||||
eval(&args[0], env)?;
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_lambda_args(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
Ok(Exp::Lambda(Lambda {
|
||||
|
@ -673,6 +1031,8 @@ fn eval_built_in_form(exp: &Exp, args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Op
|
|||
"label" | "define" | "def" => Some(eval_label_args(args, env)),
|
||||
"lambda" | "function" | "fn" => Some(eval_lambda_args(args)),
|
||||
|
||||
"set" => Some(eval_set_args(args, env)),
|
||||
"loop" => Some(eval_loop_args(args, env)),
|
||||
"defun" | "defn" => Some(eval_defun_args(args, env)),
|
||||
"apply" => Some(eval_apply_args(args, env)),
|
||||
"eval" => Some(eval_eval_args(args, env)),
|
||||
|
@ -698,6 +1058,22 @@ fn env_get(key: &str, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<(), Err> {
|
||||
let mut env = env.borrow_mut();
|
||||
match env.data.get(key) {
|
||||
Some(_) => {
|
||||
env.data.insert(key.to_string(), val);
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
match &env.outer {
|
||||
Some(outer_env) => env_set(key, val, outer_env.borrow()),
|
||||
None => Err(Err::Reason(format!("Unexpected symbol '{}'", key))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lambda_env(params: Rc<Exp>, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
let ks = list_of_symbols(¶ms)?;
|
||||
if ks.len() != args.len() {
|
||||
|
@ -718,7 +1094,7 @@ fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, Err>
|
|||
|
||||
fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
match exp {
|
||||
Exp::Sym(key) => env_get(&key, &env),
|
||||
Exp::Sym(key) => env_get(key, env),
|
||||
Exp::Bool(_) => Ok(exp.clone()),
|
||||
Exp::Num(_) => Ok(exp.clone()),
|
||||
Exp::Str(_) => Ok(exp.clone()),
|
||||
|
@ -888,7 +1264,8 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(eq \"a\" 'b)"), "false");
|
||||
assert_eq!(eval!("(eq 1 1)"), "true");
|
||||
assert_eq!(eval!("(eq 1 2)"), "false");
|
||||
assert_eq!(eval!("(eq 1 1.0)"), "true");
|
||||
assert_eq!(eval!("(eq 1 1.0)"), "false");
|
||||
assert_eq!(eval!("(eq 1.0 1.0)"), "true");
|
||||
|
||||
// car
|
||||
assert_eq!(eval!("(car (quote (1)))"), "1");
|
||||
|
@ -949,9 +1326,11 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(* 2 (* 3 4))"), "24");
|
||||
|
||||
// division
|
||||
assert_eq!(eval!("(/ 4)"), "0.25");
|
||||
assert_eq!(eval!("(/ 4)"), "0");
|
||||
assert_eq!(eval!("(/ 4.0)"), "0.25");
|
||||
assert_eq!(eval!("(/ 4 2)"), "2");
|
||||
assert_eq!(eval!("(/ 1 2)"), "0.5");
|
||||
assert_eq!(eval!("(/ 1 2)"), "0");
|
||||
assert_eq!(eval!("(/ 1 2.0)"), "0.5");
|
||||
assert_eq!(eval!("(/ 8 4 2)"), "1");
|
||||
|
||||
// exponential
|
||||
|
@ -963,13 +1342,17 @@ fn test_lisp() {
|
|||
|
||||
// comparisons
|
||||
assert_eq!(eval!("(< 6 4)"), "false");
|
||||
assert_eq!(eval!("(> 6 4 3 1)"), "true");
|
||||
assert_eq!(eval!("(> 6 4)"), "true");
|
||||
assert_eq!(eval!("(> 6 4 2)"), "true");
|
||||
assert_eq!(eval!("(> 6)"), "true");
|
||||
assert_eq!(eval!("(>)"), "true");
|
||||
assert_eq!(eval!("(= 6 4)"), "false");
|
||||
assert_eq!(eval!("(= 6 6)"), "true");
|
||||
assert_eq!(eval!("(= (+ 0.15 0.15) (+ 0.1 0.2))"), "true");
|
||||
assert_eq!(eval!("(= (+ 0.15 0.15) (+ 0.1 0.2))"), "false"); // FIXME?
|
||||
|
||||
// number
|
||||
assert_eq!(eval!("(bytes->number (number->bytes 42))"), "42");
|
||||
assert_eq!(eval!("(bytes->number (number->bytes 42) \"int\")"), "42");
|
||||
assert_eq!(eval!("(bytes->number (number->bytes 42.0) \"float\")"), "42.0");
|
||||
|
||||
// string
|
||||
assert_eq!(eval!("(parse \"9.75\")"), "9.75");
|
||||
|
@ -990,14 +1373,32 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(acos (cos pi))"), PI.to_string());
|
||||
assert_eq!(eval!("(acos 0)"), (PI / 2.0).to_string());
|
||||
assert_eq!(eval!("(asin 1)"), (PI / 2.0).to_string());
|
||||
assert_eq!(eval!("(atan 0)"), "0");
|
||||
assert_eq!(eval!("(cos pi)"), "-1");
|
||||
assert_eq!(eval!("(sin (/ pi 2))"), "1");
|
||||
assert_eq!(eval!("(tan 0)"), "0");
|
||||
assert_eq!(eval!("(atan 0)"), "0.0");
|
||||
assert_eq!(eval!("(cos pi)"), "-1.0");
|
||||
assert_eq!(eval!("(sin (/ pi 2))"), "1.0");
|
||||
assert_eq!(eval!("(tan 0)"), "0.0");
|
||||
|
||||
// list
|
||||
assert_eq!(eval!("(list)"), "()");
|
||||
assert_eq!(eval!("(list 1)"), "(1)");
|
||||
assert_eq!(eval!("(list 1 2)"), "(1 2)");
|
||||
assert_eq!(eval!("(list 1 2 (+ 1 2))"), "(1 2 3)");
|
||||
|
||||
// bigint
|
||||
assert_eq!(eval!("9223372036854775807"), "9223372036854775807"); // -> int
|
||||
assert_eq!(eval!("9223372036854775808"), "9223372036854775808"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 0)"), "9223372036854775807"); // -> int
|
||||
assert_eq!(eval!("(- 9223372036854775808 1)"), "9223372036854775807"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 1)"), "9223372036854775808"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 1.0)"), "9223372036854776000.0"); // -> float
|
||||
assert_eq!(eval!("(+ 9223372036854775807 10)"), "9223372036854775817"); // -> bigint
|
||||
assert_eq!(eval!("(* 9223372036854775807 10)"), "92233720368547758070"); // -> bigint
|
||||
|
||||
assert_eq!(eval!("(^ 2 16)"), "65536"); // -> int
|
||||
assert_eq!(eval!("(^ 2 128)"), "340282366920938463463374607431768211456"); // -> bigint
|
||||
assert_eq!(eval!("(^ 2.0 128)"), "340282366920938500000000000000000000000.0"); // -> float
|
||||
|
||||
assert_eq!(eval!("(number-type 9223372036854775807)"), "\"int\"");
|
||||
assert_eq!(eval!("(number-type 9223372036854775808)"), "\"bigint\"");
|
||||
assert_eq!(eval!("(number-type 9223372036854776000.0)"), "\"float\"");
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ pub mod list;
|
|||
pub mod memory;
|
||||
pub mod net;
|
||||
pub mod pci;
|
||||
pub mod pi;
|
||||
pub mod pow;
|
||||
pub mod r#move;
|
||||
pub mod read;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use crate::{usr, sys};
|
||||
use crate::api::process::ExitCode;
|
||||
|
||||
use alloc::format;
|
||||
use num_bigint::BigInt;
|
||||
use usr::lisp::Number;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
let mut digits = None;
|
||||
if args.len() == 2 {
|
||||
if let Ok(n) = args[1].parse() {
|
||||
digits = Some(n);
|
||||
}
|
||||
}
|
||||
|
||||
let mut q = BigInt::from(1);
|
||||
let mut r = BigInt::from(0);
|
||||
let mut t = BigInt::from(1);
|
||||
let mut k = BigInt::from(1);
|
||||
let mut n = BigInt::from(3);
|
||||
let mut l = BigInt::from(3);
|
||||
let mut first = true;
|
||||
loop {
|
||||
if sys::console::end_of_text() {
|
||||
break;
|
||||
}
|
||||
if &q * 4 + &r - &t < &n * &t {
|
||||
print!("{}", Number::BigInt(n.clone()));
|
||||
if first {
|
||||
print!(".");
|
||||
first = false;
|
||||
}
|
||||
match digits {
|
||||
Some(0) => break,
|
||||
Some(i) => digits = Some(i - 1),
|
||||
None => {},
|
||||
}
|
||||
|
||||
let nr = (&r - &n * &t) * 10;
|
||||
n = (&q * 3 + &r) * 10 / &t - &n * 10;
|
||||
q *= 10;
|
||||
r = nr;
|
||||
} else {
|
||||
let nr = (&q * 2 + &r) * &l;
|
||||
let nn = (&q * &k * 7 + 2 + &r * &l) / (&t * &l);
|
||||
q *= &k;
|
||||
t *= &l;
|
||||
l += 2;
|
||||
k += 1;
|
||||
n = nn;
|
||||
r = nr;
|
||||
}
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
|
@ -471,6 +471,7 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> Result<(), ExitCode> {
|
|||
"move" => usr::r#move::main(&args),
|
||||
"net" => usr::net::main(&args),
|
||||
"pci" => usr::pci::main(&args),
|
||||
"pi" => usr::pi::main(&args),
|
||||
"proc" => cmd_proc(&args),
|
||||
"quit" => Err(ExitCode::ShellExit),
|
||||
"read" => usr::read::main(&args),
|
||||
|
|
Loading…
Reference in New Issue