Basic floating point support (WIP) #18
@ -36,6 +36,7 @@ pub enum Literal {
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
Int(u128),
|
||||
Float(u64),
|
||||
String(String),
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ mod display {
|
||||
Literal::Bool(v) => v.fmt(f),
|
||||
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
||||
Literal::Int(v) => v.fmt(f),
|
||||
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
|
||||
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ pub trait Fold {
|
||||
fn fold_int(&mut self, i: u128) -> u128 {
|
||||
i
|
||||
}
|
||||
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
|
||||
f
|
||||
}
|
||||
fn fold_string(&mut self, s: String) -> String {
|
||||
s
|
||||
}
|
||||
@ -384,6 +387,7 @@ pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Litera
|
||||
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
|
||||
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
|
||||
Literal::Int(i) => Literal::Int(folder.fold_int(i)),
|
||||
Literal::Float(f) => Literal::Float(folder.fold_smuggled_float(f)),
|
||||
Literal::String(s) => Literal::String(folder.fold_string(s)),
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ pub trait Visit<'a>: Sized {
|
||||
fn visit_bool(&mut self, _b: &'a bool) {}
|
||||
fn visit_char(&mut self, _c: &'a char) {}
|
||||
fn visit_int(&mut self, _i: &'a u128) {}
|
||||
fn visit_smuggled_float(&mut self, _f: &'a u64) {}
|
||||
fn visit_string(&mut self, _s: &'a str) {}
|
||||
fn visit_file(&mut self, f: &'a File) {
|
||||
let File { items } = f;
|
||||
@ -339,6 +340,7 @@ pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
|
||||
Literal::Bool(b) => visitor.visit_bool(b),
|
||||
Literal::Char(c) => visitor.visit_char(c),
|
||||
Literal::Int(i) => visitor.visit_int(i),
|
||||
Literal::Float(f) => visitor.visit_smuggled_float(f),
|
||||
Literal::String(s) => visitor.visit_string(s),
|
||||
}
|
||||
}
|
||||
|
@ -223,6 +223,7 @@ builtins! {
|
||||
Ok(match tail {
|
||||
ConValue::Empty => ConValue::Empty,
|
||||
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
|
||||
ConValue::Float(v) => ConValue::Float(-v),
|
||||
_ => Err(Error::TypeError)?,
|
||||
})
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ pub enum ConValue {
|
||||
Empty,
|
||||
/// An integer
|
||||
Int(Integer),
|
||||
/// A floating point number
|
||||
Float(f64),
|
||||
/// A boolean
|
||||
Bool(bool),
|
||||
/// A unicode character
|
||||
@ -124,6 +126,7 @@ macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
||||
match (self, other) {
|
||||
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
||||
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
|
||||
(Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
|
||||
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
|
||||
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
|
||||
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||
@ -150,6 +153,7 @@ impl From<&Sym> for ConValue {
|
||||
}
|
||||
from! {
|
||||
Integer => ConValue::Int,
|
||||
f64 => ConValue::Float,
|
||||
bool => ConValue::Bool,
|
||||
char => ConValue::Char,
|
||||
Sym => ConValue::String,
|
||||
@ -189,7 +193,8 @@ ops! {
|
||||
Add: add = [
|
||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
|
||||
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
|
||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
|
||||
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
|
||||
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
|
||||
(ConValue::Char(a), ConValue::Char(b)) => {
|
||||
ConValue::String([a, b].into_iter().collect::<String>().into())
|
||||
@ -219,18 +224,21 @@ ops! {
|
||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
|
||||
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
||||
})),
|
||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
|
||||
_ => Err(Error::TypeError)?
|
||||
]
|
||||
Mul: mul = [
|
||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
|
||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
|
||||
_ => Err(Error::TypeError)?
|
||||
]
|
||||
Rem: rem = [
|
||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
|
||||
eprintln!("Warning: Divide by zero in {a} % {b}"); a
|
||||
println!("Warning: Divide by zero in {a} % {b}"); a
|
||||
})),
|
||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
|
||||
_ => Err(Error::TypeError)?
|
||||
]
|
||||
Shl: shl = [
|
||||
@ -246,6 +254,7 @@ ops! {
|
||||
Sub: sub = [
|
||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
|
||||
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
|
||||
_ => Err(Error::TypeError)?
|
||||
]
|
||||
}
|
||||
@ -254,6 +263,7 @@ impl std::fmt::Display for ConValue {
|
||||
match self {
|
||||
ConValue::Empty => "Empty".fmt(f),
|
||||
ConValue::Int(v) => v.fmt(f),
|
||||
ConValue::Float(v) => v.fmt(f),
|
||||
ConValue::Bool(v) => v.fmt(f),
|
||||
ConValue::Char(v) => v.fmt(f),
|
||||
ConValue::String(v) => v.fmt(f),
|
||||
|
@ -358,6 +358,9 @@ fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
||||
ConValue::Bool(b) => b as _,
|
||||
ConValue::Char(c) => c as _,
|
||||
ConValue::Ref(v) => return cast((*v).clone(), ty),
|
||||
// TODO: This, better
|
||||
ConValue::Float(_) if ty.starts_with('f') => return Ok(value),
|
||||
ConValue::Float(f) => f as _,
|
||||
_ => Err(Error::TypeError)?,
|
||||
};
|
||||
Ok(match &*ty {
|
||||
@ -369,6 +372,8 @@ fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
||||
"i32" => ConValue::Int(value as i32 as _),
|
||||
"u64" => ConValue::Int(value),
|
||||
"i64" => ConValue::Int(value),
|
||||
"f32" => ConValue::Float(value as f32 as _),
|
||||
"f64" => ConValue::Float(value as f64 as _),
|
||||
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
|
||||
"bool" => ConValue::Bool(value < 0),
|
||||
_ => Err(Error::NotDefined(ty))?,
|
||||
@ -448,7 +453,7 @@ impl Interpret for Literal {
|
||||
Literal::String(value) => ConValue::from(value.as_str()),
|
||||
Literal::Char(value) => ConValue::Char(*value),
|
||||
Literal::Bool(value) => ConValue::Bool(*value),
|
||||
// Literal::Float(value) => todo!("Float values in interpreter: {value:?}"),
|
||||
Literal::Float(value) => ConValue::Float(f64::from_bits(*value)),
|
||||
Literal::Int(value) => ConValue::Int(*value as _),
|
||||
})
|
||||
}
|
||||
|
@ -378,16 +378,32 @@ impl<'t> Lexer<'t> {
|
||||
Ok('d') => self.consume()?.digits::<10>(),
|
||||
Ok('o') => self.consume()?.digits::<8>(),
|
||||
Ok('b') => self.consume()?.digits::<2>(),
|
||||
Ok('0'..='9') => self.digits::<10>(),
|
||||
Ok('0'..='9' | '.') => self.digits::<10>(),
|
||||
_ => self.produce(Kind::Literal, 0),
|
||||
}
|
||||
}
|
||||
fn digits<const B: u32>(&mut self) -> LResult<Token> {
|
||||
let mut value = self.digit::<B>()? as u128;
|
||||
let mut value = 0;
|
||||
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
|
||||
value = value * B as u128 + self.digit::<B>()? as u128;
|
||||
}
|
||||
self.produce(Kind::Literal, value)
|
||||
// TODO: find a better way to handle floats in the tokenizer
|
||||
match self.peek() {
|
||||
Ok('.') => {
|
||||
// FIXME: hack: 0.. is not [0.0, '.']
|
||||
if let Ok('.') = self.clone().consume()?.next() {
|
||||
return self.produce(Kind::Literal, value);
|
||||
}
|
||||
let mut float = format!("{value}.");
|
||||
self.consume()?;
|
||||
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_digit) {
|
||||
float.push(self.iter.next().unwrap_or_default());
|
||||
}
|
||||
let float = f64::from_str(&float).expect("must be parsable as float");
|
||||
self.produce(Kind::Literal, float)
|
||||
}
|
||||
_ => self.produce(Kind::Literal, value),
|
||||
}
|
||||
}
|
||||
fn digit<const B: u32>(&mut self) -> LResult<u32> {
|
||||
let digit = self.peek()?;
|
||||
|
@ -245,7 +245,7 @@ impl Parse<'_> for Literal {
|
||||
TokenData::String(v) => Literal::String(v),
|
||||
TokenData::Character(v) => Literal::Char(v),
|
||||
TokenData::Integer(v) => Literal::Int(v),
|
||||
TokenData::Float(v) => todo!("Literal::Float({v})"),
|
||||
TokenData::Float(v) => Literal::Float(v.to_bits()),
|
||||
_ => panic!("Expected token data for {ty:?}"),
|
||||
})
|
||||
}
|
||||
|
@ -247,9 +247,9 @@ fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Precedence {
|
||||
Assign,
|
||||
Logic,
|
||||
Compare,
|
||||
Range,
|
||||
Logic,
|
||||
Bitwise,
|
||||
Shift,
|
||||
Factor,
|
||||
|
17
repline/examples/repl_float.rs
Normal file
17
repline/examples/repl_float.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! Demonstrates the use of [read_and()]:
|
||||
//!
|
||||
//! The provided closure:
|
||||
//! 1. Takes a line of input (a [String])
|
||||
//! 2. Performs some calculation (using [FromStr])
|
||||
//! 3. Returns a [Result] containing a [Response] or an [Err]
|
||||
|
||||
use repline::{prebaked::read_and, Response};
|
||||
use std::{error::Error, str::FromStr};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
read_and("\x1b[33m", " >", " ?>", |line| {
|
||||
println!("-> {:?}", f64::from_str(line.trim())?);
|
||||
Ok(Response::Accept)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
56
sample-code/sqrt.cl
Executable file
56
sample-code/sqrt.cl
Executable file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env conlang-run
|
||||
//! Square root approximation, and example applications
|
||||
|
||||
/// A really small nonzero number
|
||||
const EPSILON: f64 = 8.8541878188 / 1000000000000.0;
|
||||
|
||||
/// Calcuates the absolute value of a number
|
||||
fn f64_abs(n: f64) -> f64 {
|
||||
let n = n as f64
|
||||
if n < (0.0) { -n } else { n }
|
||||
}
|
||||
|
||||
/// Square root approximation using Newton's method
|
||||
fn sqrt(n: f64) -> f64 {
|
||||
let n = n as f64
|
||||
if n < 0.0 {
|
||||
return 0.0 / 0.0 // TODO: NaN constant
|
||||
}
|
||||
if n == 0.0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
let z = n
|
||||
loop {
|
||||
let adj = (z * z - n) / (2.0 * z)
|
||||
z -= adj
|
||||
if adj.f64_abs() < EPSILON {
|
||||
break z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pythagorean theorem: a² + b² = c²
|
||||
fn pythag(a: f64, b: f64) -> f64 {
|
||||
sqrt(a * a + b * b)
|
||||
}
|
||||
|
||||
/// Quadratic formula: (-b ± √(b² - 4ac)) / 2a
|
||||
fn quadratic(a: f64, b: f64, c: f64) -> (f64, f64) {
|
||||
let a = a as f64; let b = b as f64; let c = c as f64;
|
||||
(
|
||||
(-b + sqrt(b * b - 4.0 * a * c)) / 2.0 * a,
|
||||
(-b - sqrt(b * b - 4.0 * a * c)) / 2.0 * a,
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for i in 0..10 {
|
||||
println("sqrt(",i,") ≅ ",sqrt(i as f64))
|
||||
}
|
||||
println("\nPythagorean Theorem")
|
||||
println("Hypotenuse of ⊿(5, 12): ", pythag(5.0, 12.0))
|
||||
|
||||
println("\nQuadratic formula")
|
||||
println("Roots of 10x² + 4x - 1: ", quadratic(10.0, 44.0, -1.0))
|
||||
}
|
Loading…
Reference in New Issue
Block a user