fix imaginary, refactor

This commit is contained in:
trimill 2024-11-03 14:55:49 -05:00
parent 0560847753
commit 59f3e320e3
6 changed files with 214 additions and 113 deletions

View file

@ -63,7 +63,8 @@ impl Highlighter for TalcHelper {
| TokenKind::True
| TokenKind::False
| TokenKind::Integer
| TokenKind::Float => "\x1b[93m",
| TokenKind::Float
| TokenKind::Imaginary => "\x1b[93m",
TokenKind::String => "\x1b[92m",
TokenKind::Symbol => "\x1b[96m",
_ => "",

View file

@ -76,11 +76,11 @@ impl Display for Exception {
#[macro_export]
macro_rules! exception {
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
($exc_ty:expr, $($t:tt)*) => {
$crate::exception::Exception::new_with_msg(
$exc_ty,
$crate::lstring::LString::from(
format!($fstr, $($arg),*)
format!($($t)*)
).into()
)
};

View file

@ -67,6 +67,7 @@ pub enum TokenKind {
Identifier,
Integer,
Float,
Imaginary,
String,
Symbol,
And,
@ -154,6 +155,7 @@ impl TokenKind {
K::Identifier => "identifier",
K::Integer => "integer",
K::Float => "float",
K::Imaginary => "imaginary",
K::String => "string",
K::Symbol => "symbol",
K::And => "'and'",
@ -324,6 +326,15 @@ impl<'s> Lexer<'s> {
}
}
fn next_imag(&mut self) -> Result<Token<'s>> {
self.next()?;
if is_xid_start(self.peek()?) {
self.unexpected()
} else {
self.emit(K::Imaginary)
}
}
fn next_float(&mut self, mut has_e: bool) -> Result<Token<'s>> {
while !has_e {
while matches!(self.peek()?, '_' | '0'..='9') {
@ -331,6 +342,7 @@ impl<'s> Lexer<'s> {
}
match self.peek()? {
'e' => { self.next()?; has_e = true }
'i' => return self.next_imag(),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Float)
}
@ -341,10 +353,10 @@ impl<'s> Lexer<'s> {
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
if is_xid_start(self.peek()?) {
self.unexpected()
} else {
self.emit(K::Float)
match self.peek()? {
'i' => self.next_imag(),
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Float)
}
}
@ -356,8 +368,8 @@ impl<'s> Lexer<'s> {
'o' => { self.next()?; return self.next_int_base(8) },
's' => { self.next()?; return self.next_int_base(6) },
'b' => { self.next()?; return self.next_int_base(2) },
'0'..='9' | '.' | 'e' | 'i' => (),
c if is_xid_start(c) => return self.unexpected(),
'0'..='9' => (),
_ => return self.emit(K::Integer)
}
}
@ -367,6 +379,7 @@ impl<'s> Lexer<'s> {
match self.peek()? {
'r' => todo!("arbitrary radix integer literals"),
'e' => { self.next()?; self.next_float(true) },
'i' => self.next_imag(),
'.' => {
if self.peek_n(1) == Some('.') {
self.emit(K::Integer)

View file

@ -4,6 +4,7 @@ use std::iter::Peekable;
use crate::{lstr, lstring::LStr, symbol::Symbol, value::Value};
use super::{ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind};
use num_complex::Complex64;
use TokenKind as T;
use ExprKind as E;
@ -179,6 +180,7 @@ impl TokenKind {
| T::Try
| T::Integer
| T::Float
| T::Imaginary
| T::String
| T::Symbol
| T::True
@ -371,6 +373,11 @@ impl<'s> Parser<'s> {
.span_err(tok.span)?;
Ok(E::Literal(x.into()).span(tok.span))
},
T::Imaginary => {
let x = parse_float(&tok.content[..tok.content.len()-1])
.span_err(tok.span)?;
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
},
T::String => {
let inner = &tok.content[1..tok.content.len()-1];
let s = if &tok.content[..1] == "\"" {
@ -554,7 +561,6 @@ impl<'s> Parser<'s> {
fn parse_assign(&mut self) -> Result<Expr<'s>> {
if let Some(tok) = try_next!(self, T::Global | T::Var) {
self.next()?;
let name = expect!(self, T::Identifier);
expect!(self, T::Equal);
let val = self.parse_or()?;

View file

@ -5,7 +5,7 @@ use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use crate::{exception::{exception, throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
@ -40,7 +40,6 @@ impl Value {
}
}
#[expect(clippy::cast_precision_loss)]
pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
@ -61,6 +60,11 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
}
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value {
type Output = Result<Self>;
fn neg(self) -> Self::Output {
@ -83,74 +87,65 @@ impl Neg for Value {
}
}
impl Add<Value> for Value {
type Output = Result<Self>;
fn add(self, rhs: Value) -> Self::Output {
impl Value {
pub fn abs(self) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_add(y) {
Ok(V::Int(v))
match self {
V::Int(x) => if let Some(x) = x.checked_abs() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when adding {a} and {b}")
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_add(y) {
Ok(V::Ratio(v))
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when adding {a} and {b}")
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}")
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
}
V::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
}
}
}
impl Sub<Value> for Value {
type Output = Result<Self>;
fn sub(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_sub(y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}")
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_sub(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}")
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}")
}
}
/////////////////////////
// binary arithmetic //
/////////////////////////
macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value {
type Output = Result<Self>;
fn $name(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
}
}
}
};
}
impl Mul<Value> for Value {
type Output = Result<Self>;
fn mul(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_mul(y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when multiplying {a} and {b}")
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_mul(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when multiplying {a} and {b}")
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot multiply {l:#} and {r:#}")
}
}
}
impl_value_arith!(Add, add, checked_add, +, "add");
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
impl Div<Value> for Value {
type Output = Result<Self>;
@ -174,42 +169,35 @@ impl Div<Value> for Value {
}
}
///////////////////////////////////
// modulo and integer division //
///////////////////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)?
} else {
*r
};
Some((a, r.signum()))
}
#[expect(clippy::cast_sign_loss)]
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
}
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
}
}
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
todo!()
}
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
todo!()
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
}
#[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
@ -267,7 +255,42 @@ impl Value {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
}
}
}
//////////////////////
// exponentiation //
//////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
}
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
}
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
}
}
impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
@ -304,6 +327,12 @@ impl Value {
}
}
//////////////////////////
// Bitwise operations //
//////////////////////////
impl Shl<Value> for Value {
type Output = Result<Value>;
@ -364,8 +393,13 @@ impl BitOr<Value> for Value {
}
}
/////////////////////////////
// Equality and ordering //
/////////////////////////////
impl PartialEq for Value {
#[expect(clippy::cast_precision_loss)]
fn eq(&self, other: &Self) -> bool {
use Value as V;
use super::range::RangeType as Rty;
@ -408,7 +442,6 @@ impl PartialEq for Value {
}
impl PartialOrd for Value {
#[expect(clippy::cast_precision_loss)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value as V;
match (self, other) {
@ -431,6 +464,12 @@ impl PartialOrd for Value {
}
}
////////////
// misc //
////////////
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
match self.partial_cmp(other) {

View file

@ -67,6 +67,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into());
vm.set_global_name("totient", totient().into());
vm.set_global_name("min", min().into());
vm.set_global_name("max", max().into());
@ -364,7 +365,7 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if g == 0 {
Ok(Value::from(0))
} else {
Value::from(x/g) * Value::from(y)
(Value::from(x)/Value::from(g))? * Value::from(y)
}
}
@ -381,7 +382,11 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
let g = gcd_inner(l, a);
if g == 0 { return Ok(Value::from(0)) };
l = (l/g).wrapping_mul(a);
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
};
l = new_l;
}
Ok(Value::from(l))
@ -391,7 +396,7 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else {
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}")
throw!(*SYM_TYPE_ERROR, "factors expected integer argument, got {x:#}")
};
let mut factors = Vec::new();
if x <= 1 {
@ -424,6 +429,49 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(factors.into())
}
fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "totient expected integer argument, got {x:#}")
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 { x >>= 1; }
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
}
totient *= totient_prime(&mut x, 3);
let mut i = 5;
while x >= i*i {
totient *= totient_prime(&mut x, i);
i += 2;
totient *= totient_prime(&mut x, i);
i += 4;
}
if x > 1 {
totient *= x - 1;
}
Ok((totient as i64).into())
}
//
// numeric operations
@ -669,22 +717,16 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
match x {
Value::Int(x) => Ok(Value::Int(x.abs())),
Value::Ratio(x) => Ok(Value::Ratio(if x < 0.into() { -x } else { x })),
Value::Float(x) => Ok(Value::Float(x.abs())),
Value::Complex(x) => Ok(Value::Float(x.norm())),
x => throw!(*SYM_TYPE_ERROR, "abs expected numeric argument, got {x:#}"),
}
x.abs()
}
#[native_func(1)]
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
match x {
Value::Int(x) => Ok(Value::Int(x * x)),
Value::Ratio(x) => Ok(Value::Ratio(x * x)),
Value::Float(x) => Ok(Value::Float(x * x)),
Value::Int(_) => x.clone() * x,
Value::Ratio(_) => x.clone() * x,
Value::Float(_) => x.clone() * x,
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
}