This commit is contained in:
2024-07-18 16:07:36 +02:00
parent 949b3758fd
commit cc3699bbe7
31 changed files with 1021 additions and 312 deletions

View File

@@ -17,5 +17,6 @@ orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
ordered-float = "4.2.0"
rust-embed = "8.3.0"
rust_decimal = "1.35.0"
substack = "1.1.0"
trait-set = "0.3.0"

10
orchid-base/src/as_api.rs Normal file
View File

@@ -0,0 +1,10 @@
use orchid_api_traits::Coding;
pub trait AsApi: Sized {
type Api: Sized;
type Ctx<'a>;
fn to_api(&self, ctx: Self::Ctx<'_>) -> Self::Api;
fn into_api(self, ctx: Self::Ctx<'_>) -> Self::Api { self.to_api(ctx) }
fn from_api_ref(api: &Self::Api, ctx: Self::Ctx<'_>) -> Self;
fn from_api(api: Self::Api, ctx: Self::Ctx<'_>) -> Self { Self::from_api_ref(&api, ctx) }
}

View File

@@ -27,22 +27,26 @@ impl ErrorPosition {
location: self.position.to_api(),
}
}
pub fn new(msg: &str, position: Pos) -> Self {
Self { message: Some(Arc::new(msg.to_string())), position }
}
}
impl From<Pos> for ErrorPosition {
fn from(origin: Pos) -> Self { Self { position: origin, message: None } }
}
pub struct ErrorDetails {
#[derive(Clone)]
pub struct OwnedError {
pub description: Tok<String>,
pub message: Arc<String>,
pub locations: Vec<ErrorPosition>,
pub positions: Vec<ErrorPosition>,
}
impl ErrorDetails {
impl OwnedError {
pub fn from_api(err: &ProjErr) -> Self {
Self {
description: deintern(err.description),
message: err.message.clone(),
locations: err.locations.iter().map(ErrorPosition::from_api).collect(),
positions: err.locations.iter().map(ErrorPosition::from_api).collect(),
}
}
}

View File

@@ -5,6 +5,7 @@ pub mod event;
pub mod msg;
// pub mod gen;
pub mod api_utils;
pub mod as_api;
pub mod box_cow;
pub mod char_filter;
pub mod error;
@@ -13,7 +14,8 @@ pub mod interner;
pub mod join;
pub mod location;
pub mod name;
pub mod number;
pub mod reqnot;
pub mod sequence;
pub mod tokens;
pub mod tree;
// pub mod virt_fs;

150
orchid-base/src/number.rs Normal file
View File

@@ -0,0 +1,150 @@
use std::num::IntErrorKind;
use std::ops::Range;
use ordered_float::NotNan;
use rust_decimal::Decimal;
/// A number, either floating point or unsigned int, parsed by Orchid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Numeric {
/// A nonnegative integer
Uint(u64),
/// A binary float other than NaN
Float(NotNan<f64>),
/// A decimal number
Decimal(Decimal),
}
impl Numeric {
pub fn decimal(num: i64, scale: u32) -> Self { Self::Decimal(Decimal::new(num, scale)) }
pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) }
}
/// Rasons why [parse_num] might fail. See [NumError].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NumErrorKind {
/// The literal describes [f64::NAN]
NaN,
/// Some integer appearing in the literal overflows [usize]
Overflow,
/// A character that isn't a digit in the given base was found
InvalidDigit,
}
impl NumErrorKind {
fn from_int(kind: &IntErrorKind) -> Self {
match kind {
IntErrorKind::InvalidDigit => Self::InvalidDigit,
IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => Self::Overflow,
_ => panic!("Impossible error condition"),
}
}
}
/// Error produced by [parse_num]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NumError {
/// Location
pub range: Range<usize>,
/// Reason
pub kind: NumErrorKind,
}
/// Parse a numbre literal out of text
pub fn parse_num(string: &str) -> Result<Numeric, NumError> {
let overflow_err = NumError { range: 0..string.len(), kind: NumErrorKind::Overflow };
let (radix, noprefix, pos) = (string.strip_prefix("0x").map(|s| (16u8, s, 2)))
.or_else(|| string.strip_prefix("0b").map(|s| (2u8, s, 2)))
.or_else(|| string.strip_prefix("0o").map(|s| (8u8, s, 2)))
.unwrap_or((10u8, string, 0));
// identity
let (base, exponent) = match noprefix.split_once('p') {
Some((b, e)) => {
let (s, d, len) = e.strip_prefix('-').map_or((1, e, 0), |ue| (-1, ue, 1));
(b, s * int_parse(d, 10, pos + b.len() + 1 + len)? as i32)
},
None => (noprefix, 0),
};
match base.split_once('.') {
None => {
let base_usize = int_parse(base, radix, pos)?;
if let Ok(pos_exp) = u32::try_from(exponent) {
if let Some(radical) = u64::from(radix).checked_pow(pos_exp) {
let number = base_usize.checked_mul(radical).ok_or(overflow_err)?;
return Ok(Numeric::Uint(number));
}
}
let f = (base_usize as f64) * (radix as f64).powi(exponent);
let err = NumError { range: 0..string.len(), kind: NumErrorKind::NaN };
Ok(Numeric::Float(NotNan::new(f).map_err(|_| err)?))
},
Some((whole, part)) => {
let whole_n = int_parse(whole, radix, pos)?;
let part_n = int_parse(part, radix, pos + whole.len() + 1)?;
let scale = part.chars().filter(|c| *c != '_').count() as u32;
if radix == 10 {
let mut scaled_unit = Decimal::ONE;
(scaled_unit.set_scale(scale))
.map_err(|_| NumError { range: 0..string.len(), kind: NumErrorKind::Overflow })?;
Ok(Numeric::Decimal(Decimal::from(whole_n) + scaled_unit * Decimal::from(part_n)))
} else {
let real_val = whole_n as f64 + (part_n as f64 / (radix as f64).powi(scale as i32));
let f = real_val * (radix as f64).powi(exponent);
Ok(Numeric::Float(NotNan::new(f).expect("None of the inputs are NaN")))
}
},
}
}
fn int_parse(s: &str, radix: u8, start: usize) -> Result<u64, NumError> {
let s = s.chars().filter(|c| *c != '_').collect::<String>();
let range = start..(start + s.len());
u64::from_str_radix(&s, radix as u32)
.map_err(|e| NumError { range, kind: NumErrorKind::from_int(e.kind()) })
}
/// Filter for characters that can appear in numbers
pub fn numchar(c: char) -> bool { c.is_alphanumeric() | "._-".contains(c) }
/// Filter for characters that can start numbers
pub fn numstart(c: char) -> bool { c.is_ascii_digit() }
/// Print a number as a base-16 floating point literal
#[must_use]
pub fn print_nat16(num: NotNan<f64>) -> String {
if *num == 0.0 {
return "0x0".to_string();
} else if num.is_infinite() {
return match num.is_sign_positive() {
true => "Infinity".to_string(),
false => "-Infinity".to_string(),
};
} else if num.is_nan() {
return "NaN".to_string();
}
let exp = num.log(16.0).floor();
let man = *num / 16_f64.powf(exp);
format!("0x{man}p{exp:.0}")
}
#[cfg(test)]
mod test {
use super::{parse_num, Numeric};
#[test]
fn just_ints() {
let test = |s, n| assert_eq!(parse_num(s), Ok(Numeric::Uint(n)));
test("12345", 12345);
test("0xcafebabe", 0xcafebabe);
test("0o751", 0o751);
test("0b111000111", 0b111000111);
}
#[test]
fn decimals() {
let test = |s, n| assert_eq!(parse_num(s), Ok(n));
test("3.1417", Numeric::decimal(31417, 4));
test("0xf.cafe", Numeric::float(0xf as f64 + 0xcafe as f64 / 0x10000 as f64));
test("34p3", Numeric::Uint(34000));
test("0x2p3", Numeric::Uint(0x2 * 0x1000));
test("1.5p3", Numeric::decimal(1500, 0));
test("0x2.5p3", Numeric::float((0x25 * 0x100) as f64));
}
}

15
orchid-base/src/tokens.rs Normal file
View File

@@ -0,0 +1,15 @@
use orchid_api::tree::{Placeholder, PlaceholderKind};
use crate::interner::{deintern, Tok};
#[derive(Clone)]
pub struct OwnedPh {
pub name: Tok<String>,
pub kind: PlaceholderKind,
}
impl OwnedPh {
pub fn to_api(&self) -> Placeholder {
Placeholder { name: self.name.marker(), kind: self.kind.clone() }
}
pub fn from_api(ph: Placeholder) -> Self { Self { name: deintern(ph.name), kind: ph.kind } }
}