mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-10-26 05:50:18 +03:00
Merge pull request #654 from HigherOrderCO/648-hexadecimal-and-binary-floating-point-numbers
#648 Hexadecimal and binary floating-point numbers
This commit is contained in:
commit
5c6eb841e4
@ -28,6 +28,7 @@ and this project does not currently adhere to a particular versioning scheme.
|
||||
- Change tuple syntax to not require parentheses in some cases. ([#554][gh-554])
|
||||
- Improve error messages in branching statements. ([#464][gh-464])
|
||||
- Change branches to support ending with ask statements. ([#629][gh-629])
|
||||
- Improve hexadecimal and binary floating numbers. ([#648][gh-648])
|
||||
|
||||
## [0.2.36] - 2024-07-04
|
||||
|
||||
@ -414,4 +415,5 @@ and this project does not currently adhere to a particular versioning scheme.
|
||||
[gh-629]: https://github.com/HigherOrderCO/Bend/issues/629
|
||||
[gh-642]: https://github.com/HigherOrderCO/Bend/issues/642
|
||||
[gh-643]: https://github.com/HigherOrderCO/Bend/issues/643
|
||||
[gh-648]: https://github.com/HigherOrderCO/Bend/issues/648
|
||||
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD
|
||||
|
@ -580,6 +580,18 @@ Currently, the 3 number types cannot be mixed.
|
||||
| Bitwise Or | x \| y | int, uint |
|
||||
| Bitwise Xor | x ^ y | int, uint |
|
||||
|
||||
Hexadecimal and binary floating-point literals are also supported.
|
||||
|
||||
In these representations, each digit after the point is divided according to the base’s power of the digit's position.
|
||||
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the power of the digit's depth.
|
||||
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the power of the digit's depth.
|
||||
|
||||
```python
|
||||
0xA.A == 10.625
|
||||
|
||||
0b111.111 == 7.875
|
||||
```
|
||||
|
||||
### Constructor Literals
|
||||
|
||||
Constructors are just functions.
|
||||
@ -1141,6 +1153,18 @@ Currently, the 3 number types cannot be mixed.
|
||||
| Bitwise Or | (\| x y) | int, uint |
|
||||
| Bitwise Xor | (^ x y) | int, uint |
|
||||
|
||||
Hexadecimal and binary floating-point literals are also supported.
|
||||
|
||||
In these representations, each digit after the point is divided according to the base’s power of the digit's position.
|
||||
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the negative power of the digit's depth.
|
||||
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the negative power of the digit's depth.
|
||||
|
||||
```python
|
||||
(== 0xA.A 10.625)
|
||||
|
||||
(== 0b111.111 7.875)
|
||||
```
|
||||
|
||||
### Character Literal
|
||||
|
||||
```rust
|
||||
@ -1240,14 +1264,17 @@ changed or optimized by the compiler.
|
||||
# Import Syntax
|
||||
|
||||
### Import Relative to the File
|
||||
|
||||
Paths starting with `./` or `../` are imported relative to the file.
|
||||
|
||||
### Import Relative to the Main Folder
|
||||
|
||||
Paths that do not start with `./` or `../` are relative to the folder of the main file.
|
||||
|
||||
## Syntax
|
||||
|
||||
### Import Specific Names from a File, or Files from a Folder
|
||||
|
||||
```py
|
||||
from path import name
|
||||
from path import (name1, name2)
|
||||
@ -1255,11 +1282,13 @@ import (path/name1, path/name2)
|
||||
```
|
||||
|
||||
### Import All Names from a File, or All Files from a Folder
|
||||
|
||||
```py
|
||||
from path import *
|
||||
```
|
||||
|
||||
### Aliasing Imports
|
||||
|
||||
```py
|
||||
from path import name as alias
|
||||
from path import (name1 as Alias1, name2 as Alias2)
|
||||
|
@ -1518,80 +1518,85 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
let radix = match self.peek_many(2) {
|
||||
Some("0x") => {
|
||||
self.advance_many(2);
|
||||
16
|
||||
Radix::Hex
|
||||
}
|
||||
Some("0b") => {
|
||||
self.advance_many(2);
|
||||
2
|
||||
Radix::Bin
|
||||
}
|
||||
_ => 10,
|
||||
_ => Radix::Dec,
|
||||
};
|
||||
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
|
||||
let num_str = self.take_while(move |c| c.is_digit(radix as u32) || c == '_');
|
||||
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
|
||||
|
||||
let next_is_hex = self.peek_one().map_or(false, |c| "0123456789abcdefABCDEF".contains(c));
|
||||
if next_is_hex || num_str.is_empty() {
|
||||
let base = match radix {
|
||||
16 => "hexadecimal",
|
||||
10 => "decimal",
|
||||
2 => "binary",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.expected(format!("valid {base} digit").as_str())
|
||||
self.expected(format!("valid {radix} digit").as_str())
|
||||
} else {
|
||||
u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
|
||||
u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn u32_with_radix(&mut self, radix: Radix) -> ParseResult<u32> {
|
||||
let num_str = self.take_while(move |c| c.is_digit(radix as u32) || c == '_');
|
||||
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
|
||||
let next_is_hex = self.peek_one().map_or(false, |c| "0123456789abcdefABCDEF".contains(c));
|
||||
if next_is_hex || num_str.is_empty() {
|
||||
self.expected(format!("valid {radix} digit").as_str())
|
||||
} else {
|
||||
u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_number(&mut self) -> ParseResult<Num> {
|
||||
let ini_idx = *self.index();
|
||||
|
||||
// Parses sign
|
||||
let sgn = if self.try_consume_exactly("+") {
|
||||
let sign = if self.try_consume_exactly("+") {
|
||||
Some(1)
|
||||
} else if self.try_consume_exactly("-") {
|
||||
Some(-1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Parses main value
|
||||
let num = self.parse_u32()?;
|
||||
|
||||
// Parses frac value (Float type)
|
||||
// TODO: Will lead to some rounding errors
|
||||
// TODO: Doesn't cover very large/small numbers
|
||||
let fra = if let Some('.') = self.peek_one() {
|
||||
let radix = match self.peek_many(2) {
|
||||
Some("0x") => {
|
||||
self.advance_many(2);
|
||||
Radix::Hex
|
||||
}
|
||||
Some("0b") => {
|
||||
self.advance_many(2);
|
||||
Radix::Bin
|
||||
}
|
||||
_ => Radix::Dec,
|
||||
};
|
||||
let num = self.u32_with_radix(radix)?;
|
||||
let frac = if let Some('.') = self.peek_one() {
|
||||
self.advance_one();
|
||||
let ini_idx = *self.index();
|
||||
let fra = self.parse_u32()? as f32;
|
||||
let end_idx = *self.index();
|
||||
let fra = fra / 10f32.powi((end_idx - ini_idx) as i32);
|
||||
let fra_str = self.take_while(|c| c.is_digit(radix as u32) || c == '_');
|
||||
let fra_str = fra_str.chars().filter(|c| *c != '_').collect::<String>();
|
||||
let fra = u32::from_str_radix(&fra_str, radix as u32).map_err(|e| e.to_string())?;
|
||||
let fra = fra as f32 / (radix.to_f32()).powi(fra_str.len() as i32);
|
||||
Some(fra)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// F24
|
||||
if let Some(fra) = fra {
|
||||
let sgn = sgn.unwrap_or(1);
|
||||
return Ok(Num::F24(sgn as f32 * (num as f32 + fra)));
|
||||
if let Some(frac) = frac {
|
||||
let sign = sign.unwrap_or(1);
|
||||
return Ok(Num::F24(sign as f32 * (num as f32 + frac)));
|
||||
}
|
||||
|
||||
// I24
|
||||
if let Some(sgn) = sgn {
|
||||
let num = sgn * num as i32;
|
||||
if let Some(sign) = sign {
|
||||
let num = sign * num as i32;
|
||||
if !(-0x00800000..=0x007fffff).contains(&num) {
|
||||
return self.num_range_err(ini_idx, "I24");
|
||||
}
|
||||
return Ok(Num::I24(num));
|
||||
Ok(Num::I24(num))
|
||||
} else {
|
||||
if num >= 1 << 24 {
|
||||
return self.num_range_err(ini_idx, "U24");
|
||||
}
|
||||
Ok(Num::U24(num))
|
||||
}
|
||||
|
||||
// U24
|
||||
if num >= 1 << 24 {
|
||||
return self.num_range_err(ini_idx, "U24");
|
||||
}
|
||||
Ok(Num::U24(num))
|
||||
}
|
||||
|
||||
fn num_range_err<T>(&mut self, ini_idx: usize, typ: &str) -> ParseResult<T> {
|
||||
@ -1659,3 +1664,30 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Radix {
|
||||
Bin = 2,
|
||||
Dec = 10,
|
||||
Hex = 16,
|
||||
}
|
||||
|
||||
impl Radix {
|
||||
fn to_f32(self) -> f32 {
|
||||
match self {
|
||||
Radix::Bin => 2.,
|
||||
Radix::Dec => 10.,
|
||||
Radix::Hex => 16.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Radix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Radix::Bin => write!(f, "binary"),
|
||||
Radix::Dec => write!(f, "decimal"),
|
||||
Radix::Hex => write!(f, "hexadecimal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
tests/golden_tests/parse_file/bad_floating.bend
Normal file
2
tests/golden_tests/parse_file/bad_floating.bend
Normal file
@ -0,0 +1,2 @@
|
||||
def main:
|
||||
return 0xA.0xA
|
2
tests/golden_tests/run_file/floating_numbers.bend
Normal file
2
tests/golden_tests/run_file/floating_numbers.bend
Normal file
@ -0,0 +1,2 @@
|
||||
def main:
|
||||
return [0x12.129, 0x0.2, 0b101.101, 0xAAAAAAAA.AAAAAAAA, 0xA.__A__]
|
9
tests/snapshots/parse_file__bad_floating.bend.snap
Normal file
9
tests/snapshots/parse_file__bad_floating.bend.snap
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/parse_file/bad_floating.bend
|
||||
---
|
||||
[4m[1m[31mErrors:[0m
|
||||
In tests/golden_tests/parse_file/bad_floating.bend :
|
||||
[1m- expected:[0m newline
|
||||
[1m- detected:[0m
|
||||
[0m 2 | return 0xA.0[4m[31mx[0mA[0m
|
9
tests/snapshots/run_file__floating_numbers.bend.snap
Normal file
9
tests/snapshots/run_file__floating_numbers.bend.snap
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/floating_numbers.bend
|
||||
---
|
||||
NumScott:
|
||||
[18.072, 0.125, 5.625, 2863333376.000, 10.625]
|
||||
|
||||
Scott:
|
||||
[18.072, 0.125, 5.625, 2863333376.000, 10.625]
|
Loading…
Reference in New Issue
Block a user