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::True
| TokenKind::False | TokenKind::False
| TokenKind::Integer | TokenKind::Integer
| TokenKind::Float => "\x1b[93m", | TokenKind::Float
| TokenKind::Imaginary => "\x1b[93m",
TokenKind::String => "\x1b[92m", TokenKind::String => "\x1b[92m",
TokenKind::Symbol => "\x1b[96m", TokenKind::Symbol => "\x1b[96m",
_ => "", _ => "",

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64; use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero}; 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}; 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) { pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V; use Value as V;
match (&a, &b) { match (&a, &b) {
@ -61,6 +60,11 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
} }
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value { impl Neg for Value {
type Output = Result<Self>; type Output = Result<Self>;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
@ -83,74 +87,65 @@ impl Neg for Value {
} }
} }
impl Add<Value> for Value { impl Value {
type Output = Result<Self>; pub fn abs(self) -> Result<Self> {
fn add(self, rhs: Value) -> Self::Output {
use Value as V; use Value as V;
let (a, b) = promote(self, rhs); match self {
match (&a, &b) { V::Int(x) => if let Some(x) = x.checked_abs() {
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_add(y) { Ok(V::Int(x))
Ok(V::Int(v))
} else { } 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) { V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(v)) Ok(V::Ratio(x))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when adding {a} and {b}") throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
}, }
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)), V::Float(x) => Ok(V::Float(x.abs())),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)), V::Complex(x) => Ok(V::Float(x.norm())),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}") a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
} }
} }
} }
impl Sub<Value> for Value {
type Output = Result<Self>; /////////////////////////
fn sub(self, rhs: Value) -> Self::Output { // binary arithmetic //
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) { macro_rules! impl_value_arith {
Ok(V::Int(v)) ($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
} else {
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}") impl $trait<Value> for Value {
}, type Output = Result<Self>;
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_sub(y) { fn $name(self, rhs: Value) -> Self::Output {
Ok(V::Ratio(v)) use Value as V;
} else { let (a, b) = promote(self, rhs);
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}") match (&a, &b) {
}, (V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)), Ok(V::Int(v))
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)), } else {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}") 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 { impl_value_arith!(Add, add, checked_add, +, "add");
type Output = Result<Self>; impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
fn mul(self, rhs: Value) -> Self::Output { impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
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 Div<Value> for Value { impl Div<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
@ -174,42 +169,35 @@ impl Div<Value> for Value {
} }
} }
///////////////////////////////////
// modulo and integer division //
///////////////////////////////////
#[inline] #[inline]
fn ipow(n: i64, p: u64) -> Option<i64> { fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
match (n, p) { let a = if r.is_negative() {
(0, 0) => None, Rational64::ZERO.checked_sub(r)?
(0, _) => Some(0), } else {
(_, 0) => Some(1), *r
(1, _) => Some(1), };
(-1, p) => (-1_i64).checked_pow((p % 2) as u32), Some((a, r.signum()))
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
} }
#[expect(clippy::cast_sign_loss)]
#[inline] #[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> { 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 { impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> { pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
@ -267,7 +255,42 @@ impl Value {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}") (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> { pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) { if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
@ -304,6 +327,12 @@ impl Value {
} }
} }
//////////////////////////
// Bitwise operations //
//////////////////////////
impl Shl<Value> for Value { impl Shl<Value> for Value {
type Output = Result<Value>; type Output = Result<Value>;
@ -364,8 +393,13 @@ impl BitOr<Value> for Value {
} }
} }
/////////////////////////////
// Equality and ordering //
/////////////////////////////
impl PartialEq for Value { impl PartialEq for Value {
#[expect(clippy::cast_precision_loss)]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use Value as V; use Value as V;
use super::range::RangeType as Rty; use super::range::RangeType as Rty;
@ -408,7 +442,6 @@ impl PartialEq for Value {
} }
impl PartialOrd for Value { impl PartialOrd for Value {
#[expect(clippy::cast_precision_loss)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value as V; use Value as V;
match (self, other) { match (self, other) {
@ -431,6 +464,12 @@ impl PartialOrd for Value {
} }
} }
////////////
// misc //
////////////
impl Value { impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> { pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
match self.partial_cmp(other) { 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("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into()); vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().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("min", min().into());
vm.set_global_name("max", max().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 { if g == 0 {
Ok(Value::from(0)) Ok(Value::from(0))
} else { } 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); let g = gcd_inner(l, a);
if g == 0 { return Ok(Value::from(0)) }; 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)) 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> { pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else { 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(); let mut factors = Vec::new();
if x <= 1 { if x <= 1 {
@ -424,6 +429,49 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(factors.into()) 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 // numeric operations
@ -669,22 +717,16 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
match x { x.abs()
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:#}"),
}
} }
#[native_func(1)] #[native_func(1)]
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
match x { match x {
Value::Int(x) => Ok(Value::Int(x * x)), Value::Int(_) => x.clone() * x,
Value::Ratio(x) => Ok(Value::Ratio(x * x)), Value::Ratio(_) => x.clone() * x,
Value::Float(x) => Ok(Value::Float(x * x)), Value::Float(_) => x.clone() * x,
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())), Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"), x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
} }