Merge pull request 'Basic floating point support (WIP)' (#18) from floats into main
Reviewed-on: #18
This commit is contained in:
commit
ae11d87d68
@ -36,6 +36,7 @@ pub enum Literal {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
Char(char),
|
Char(char),
|
||||||
Int(u128),
|
Int(u128),
|
||||||
|
Float(u64),
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ mod display {
|
|||||||
Literal::Bool(v) => v.fmt(f),
|
Literal::Bool(v) => v.fmt(f),
|
||||||
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
||||||
Literal::Int(v) => v.fmt(f),
|
Literal::Int(v) => v.fmt(f),
|
||||||
|
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
|
||||||
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ pub trait Fold {
|
|||||||
fn fold_int(&mut self, i: u128) -> u128 {
|
fn fold_int(&mut self, i: u128) -> u128 {
|
||||||
i
|
i
|
||||||
}
|
}
|
||||||
|
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
|
||||||
|
f
|
||||||
|
}
|
||||||
fn fold_string(&mut self, s: String) -> String {
|
fn fold_string(&mut self, s: String) -> String {
|
||||||
s
|
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::Bool(b) => Literal::Bool(folder.fold_bool(b)),
|
||||||
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
|
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
|
||||||
Literal::Int(i) => Literal::Int(folder.fold_int(i)),
|
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)),
|
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_bool(&mut self, _b: &'a bool) {}
|
||||||
fn visit_char(&mut self, _c: &'a char) {}
|
fn visit_char(&mut self, _c: &'a char) {}
|
||||||
fn visit_int(&mut self, _i: &'a u128) {}
|
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_string(&mut self, _s: &'a str) {}
|
||||||
fn visit_file(&mut self, f: &'a File) {
|
fn visit_file(&mut self, f: &'a File) {
|
||||||
let File { items } = f;
|
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::Bool(b) => visitor.visit_bool(b),
|
||||||
Literal::Char(c) => visitor.visit_char(c),
|
Literal::Char(c) => visitor.visit_char(c),
|
||||||
Literal::Int(i) => visitor.visit_int(i),
|
Literal::Int(i) => visitor.visit_int(i),
|
||||||
|
Literal::Float(f) => visitor.visit_smuggled_float(f),
|
||||||
Literal::String(s) => visitor.visit_string(s),
|
Literal::String(s) => visitor.visit_string(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,7 @@ builtins! {
|
|||||||
Ok(match tail {
|
Ok(match tail {
|
||||||
ConValue::Empty => ConValue::Empty,
|
ConValue::Empty => ConValue::Empty,
|
||||||
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
|
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
|
||||||
|
ConValue::Float(v) => ConValue::Float(-v),
|
||||||
_ => Err(Error::TypeError)?,
|
_ => Err(Error::TypeError)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ pub enum ConValue {
|
|||||||
Empty,
|
Empty,
|
||||||
/// An integer
|
/// An integer
|
||||||
Int(Integer),
|
Int(Integer),
|
||||||
|
/// A floating point number
|
||||||
|
Float(f64),
|
||||||
/// A boolean
|
/// A boolean
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
/// A unicode character
|
/// A unicode character
|
||||||
@ -124,6 +126,7 @@ macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
|||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
||||||
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
|
(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::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
|
||||||
(Self::Char(a), Self::Char(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)),
|
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
@ -150,6 +153,7 @@ impl From<&Sym> for ConValue {
|
|||||||
}
|
}
|
||||||
from! {
|
from! {
|
||||||
Integer => ConValue::Int,
|
Integer => ConValue::Int,
|
||||||
|
f64 => ConValue::Float,
|
||||||
bool => ConValue::Bool,
|
bool => ConValue::Bool,
|
||||||
char => ConValue::Char,
|
char => ConValue::Char,
|
||||||
Sym => ConValue::String,
|
Sym => ConValue::String,
|
||||||
@ -189,7 +193,8 @@ ops! {
|
|||||||
Add: add = [
|
Add: add = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
|
(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::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
|
||||||
(ConValue::Char(a), ConValue::Char(b)) => {
|
(ConValue::Char(a), ConValue::Char(b)) => {
|
||||||
ConValue::String([a, b].into_iter().collect::<String>().into())
|
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(|| {
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
|
||||||
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
||||||
})),
|
})),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
|
||||||
_ => Err(Error::TypeError)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Mul: mul = [
|
Mul: mul = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
|
(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)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Rem: rem = [
|
Rem: rem = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
|
(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)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
Shl: shl = [
|
Shl: shl = [
|
||||||
@ -246,6 +254,7 @@ ops! {
|
|||||||
Sub: sub = [
|
Sub: sub = [
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
|
(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)?
|
_ => Err(Error::TypeError)?
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -254,6 +263,7 @@ impl std::fmt::Display for ConValue {
|
|||||||
match self {
|
match self {
|
||||||
ConValue::Empty => "Empty".fmt(f),
|
ConValue::Empty => "Empty".fmt(f),
|
||||||
ConValue::Int(v) => v.fmt(f),
|
ConValue::Int(v) => v.fmt(f),
|
||||||
|
ConValue::Float(v) => v.fmt(f),
|
||||||
ConValue::Bool(v) => v.fmt(f),
|
ConValue::Bool(v) => v.fmt(f),
|
||||||
ConValue::Char(v) => v.fmt(f),
|
ConValue::Char(v) => v.fmt(f),
|
||||||
ConValue::String(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::Bool(b) => b as _,
|
||||||
ConValue::Char(c) => c as _,
|
ConValue::Char(c) => c as _,
|
||||||
ConValue::Ref(v) => return cast((*v).clone(), ty),
|
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)?,
|
_ => Err(Error::TypeError)?,
|
||||||
};
|
};
|
||||||
Ok(match &*ty {
|
Ok(match &*ty {
|
||||||
@ -369,6 +372,8 @@ fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
|
|||||||
"i32" => ConValue::Int(value as i32 as _),
|
"i32" => ConValue::Int(value as i32 as _),
|
||||||
"u64" => ConValue::Int(value),
|
"u64" => ConValue::Int(value),
|
||||||
"i64" => 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}')),
|
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
|
||||||
"bool" => ConValue::Bool(value < 0),
|
"bool" => ConValue::Bool(value < 0),
|
||||||
_ => Err(Error::NotDefined(ty))?,
|
_ => Err(Error::NotDefined(ty))?,
|
||||||
@ -448,7 +453,7 @@ impl Interpret for Literal {
|
|||||||
Literal::String(value) => ConValue::from(value.as_str()),
|
Literal::String(value) => ConValue::from(value.as_str()),
|
||||||
Literal::Char(value) => ConValue::Char(*value),
|
Literal::Char(value) => ConValue::Char(*value),
|
||||||
Literal::Bool(value) => ConValue::Bool(*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 _),
|
Literal::Int(value) => ConValue::Int(*value as _),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -378,16 +378,32 @@ impl<'t> Lexer<'t> {
|
|||||||
Ok('d') => self.consume()?.digits::<10>(),
|
Ok('d') => self.consume()?.digits::<10>(),
|
||||||
Ok('o') => self.consume()?.digits::<8>(),
|
Ok('o') => self.consume()?.digits::<8>(),
|
||||||
Ok('b') => self.consume()?.digits::<2>(),
|
Ok('b') => self.consume()?.digits::<2>(),
|
||||||
Ok('0'..='9') => self.digits::<10>(),
|
Ok('0'..='9' | '.') => self.digits::<10>(),
|
||||||
_ => self.produce(Kind::Literal, 0),
|
_ => self.produce(Kind::Literal, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn digits<const B: u32>(&mut self) -> LResult<Token> {
|
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) {
|
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
|
||||||
value = value * B as u128 + self.digit::<B>()? as u128;
|
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> {
|
fn digit<const B: u32>(&mut self) -> LResult<u32> {
|
||||||
let digit = self.peek()?;
|
let digit = self.peek()?;
|
||||||
|
@ -245,7 +245,7 @@ impl Parse<'_> for Literal {
|
|||||||
TokenData::String(v) => Literal::String(v),
|
TokenData::String(v) => Literal::String(v),
|
||||||
TokenData::Character(v) => Literal::Char(v),
|
TokenData::Character(v) => Literal::Char(v),
|
||||||
TokenData::Integer(v) => Literal::Int(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:?}"),
|
_ => 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)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Assign,
|
Assign,
|
||||||
|
Logic,
|
||||||
Compare,
|
Compare,
|
||||||
Range,
|
Range,
|
||||||
Logic,
|
|
||||||
Bitwise,
|
Bitwise,
|
||||||
Shift,
|
Shift,
|
||||||
Factor,
|
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