Add support for hexadecimal numbers in Lisp (#540)

* Add support for hexadecimal numbers in lisp

* Add source in comment

* Add support for 0X

* Add doc

* Remove extra space in doc

* Remove plural

* Add negative number to doc

* Update doc

* Fix typo

* Add ntp client

* Add ntp to install

* Remove ntp code
This commit is contained in:
Vincent Ollivier 2023-11-17 10:08:25 +01:00 committed by GitHub
parent 74df7467cb
commit 96b20dfb51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 28 deletions

View File

@ -9,7 +9,11 @@ MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
### Types
- Basics: `bool`, `list`, `symbol`, `string`
- Numbers: `float`, `int`, `bigint`
- Number: `float`, `int`, `bigint`
### Literals
- String: `"Hello, World!"`
- Number: `2.5`, `-25`, `255`, `0xFF`, `0xDEAD_C0DE`
### Built-in Operators
- `quote` (abbreviated with `'`)
@ -203,3 +207,4 @@ language and reading from the filesystem.
- Add socket functions
### Unreleased
- Add hexadecimal number literals

View File

@ -339,6 +339,25 @@ fn test_lisp() {
};
}
// num
assert_eq!(eval!("6"), "6");
assert_eq!(eval!("16"), "16");
assert_eq!(eval!("0x6"), "6");
assert_eq!(eval!("0xf"), "15");
assert_eq!(eval!("0x10"), "16");
assert_eq!(eval!("1.5"), "1.5");
assert_eq!(eval!("0xff"), "255");
assert_eq!(eval!("0XFF"), "255");
assert_eq!(eval!("-6"), "-6");
assert_eq!(eval!("-16"), "-16");
assert_eq!(eval!("-0x6"), "-6");
assert_eq!(eval!("-0xF"), "-15");
assert_eq!(eval!("-0x10"), "-16");
assert_eq!(eval!("-1.5"), "-1.5");
assert_eq!(eval!("-0xff"), "-255");
assert_eq!(eval!("-0XFF"), "-255");
// quote
assert_eq!(eval!("(quote (1 2 3))"), "(1 2 3)");
assert_eq!(eval!("'(1 2 3)"), "(1 2 3)");
@ -500,6 +519,9 @@ fn test_lisp() {
// bigint
assert_eq!(eval!("9223372036854775807"), "9223372036854775807"); // -> int
assert_eq!(eval!("9223372036854775808"), "9223372036854775808"); // -> bigint
assert_eq!(eval!("0x7fffffffffffffff"), "9223372036854775807"); // -> int
assert_eq!(eval!("0x8000000000000000"), "9223372036854775808"); // -> bigint
assert_eq!(eval!("0x800000000000000f"), "9223372036854775823"); // -> bigint
assert_eq!(eval!("(+ 9223372036854775807 0)"), "9223372036854775807"); // -> int
assert_eq!(eval!("(- 9223372036854775808 1)"), "9223372036854775807"); // -> bigint
assert_eq!(eval!("(+ 9223372036854775807 1)"), "9223372036854775808"); // -> bigint

View File

@ -5,9 +5,12 @@ use alloc::format;
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt;
use core::num::ParseIntError;
use core::ops::{Neg, Add, Div, Mul, Sub, Rem, Shl, Shr};
use core::str::FromStr;
use num_bigint::BigInt;
use num_bigint::ParseBigIntError;
use num_traits::Num;
use num_traits::Zero;
use num_traits::cast::ToPrimitive;
@ -196,39 +199,46 @@ operator!(Rem, rem);
operator!(Shl, shl);
operator!(Shr, shr);
fn parse_int(s: &str) -> Result<i64, ParseIntError> {
if s.starts_with("0x") || s.starts_with("0X") {
i64::from_str_radix(&s[2..], 16)
} else if s.starts_with("-0x") || s.starts_with("-0X") {
i64::from_str_radix(&s[3..], 16).map(|n| -n)
} else {
i64::from_str_radix(s, 10)
}
}
fn parse_float(s: &str) -> Result<BigInt, ParseBigIntError> {
if s.starts_with("0x") || s.starts_with("0X") {
BigInt::from_str_radix(&s[2..], 16)
} else if s.starts_with("-0x") || s.starts_with("-0X") {
BigInt::from_str_radix(&s[3..], 16).map(|n| -n)
} else {
BigInt::from_str_radix(s, 10)
}
}
impl FromStr for Number {
type Err = Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let err = could_not!("parse number");
if s.is_empty() {
return Ok(Number::Int(0));
Ok(Number::Int(0))
} else if s.contains('.') {
if let Ok(n) = s.parse() {
return Ok(Number::Float(n));
Ok(Number::Float(n))
} else {
err
}
} else if let Ok(n) = s.parse() {
return Ok(Number::Int(n));
} else if let Ok(n) = parse_int(s) {
Ok(Number::Int(n))
} else if let Ok(n) = parse_float(s) {
Ok(Number::BigInt(n))
} else {
let mut chars = s.chars().peekable();
let is_neg = chars.peek() == Some(&'-');
if is_neg {
chars.next().unwrap();
}
let mut res = BigInt::from(0);
for c in chars {
if !c.is_ascii_digit() {
return err;
}
let d = c as u8 - b'0';
res = res * BigInt::from(10) + BigInt::from(d as u32);
}
res *= BigInt::from(if is_neg { -1 } else { 1 });
return Ok(Number::BigInt(res));
} /* else if let Ok(n) = s.parse() { // FIXME: rust-lld: error: undefined symbol: fmod
return Ok(Number::BigInt(n));
} */
err
err
}
}
}

View File

@ -13,7 +13,6 @@ use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::character::complete::char;
use nom::character::complete::digit1;
use nom::character::complete::multispace0;
use nom::combinator::map;
use nom::combinator::opt;
@ -23,6 +22,66 @@ use nom::multi::many0;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::character::complete::one_of;
use nom::multi::many1;
use nom::sequence::terminated;
// https://docs.rs/nom/latest/nom/recipes/index.html#hexadecimal
fn hexadecimal(input: &str) -> IResult<&str, &str> {
preceded(
alt((tag("0x"), tag("0X"))),
recognize(
many1(
terminated(one_of("0123456789abcdefABCDEF"), many0(char('_')))
)
)
)(input)
}
// https://docs.rs/nom/latest/nom/recipes/index.html#decimal
fn decimal(input: &str) -> IResult<&str, &str> {
recognize(
many1(
terminated(one_of("0123456789"), many0(char('_')))
)
)(input)
}
// https://docs.rs/nom/latest/nom/recipes/index.html#floating-point-numbers
fn float(input: &str) -> IResult<&str, &str> {
alt((
recognize( // .42
tuple((
char('.'),
decimal,
opt(tuple((
one_of("eE"),
opt(one_of("+-")),
decimal
)))
))
),
recognize( // 42e42 and 42.42e42
tuple((
decimal,
opt(preceded(
char('.'),
decimal,
)),
one_of("eE"),
opt(one_of("+-")),
decimal
))
),
recognize( // 42. and 42.42
tuple((
decimal,
char('.'),
opt(decimal)
))
)
))(input)
}
fn is_symbol_letter(c: char) -> bool {
let chars = "<>=-+*/%^?.";
@ -49,8 +108,7 @@ fn parse_sym(input: &str) -> IResult<&str, Exp> {
fn parse_num(input: &str) -> IResult<&str, Exp> {
let (input, num) = recognize(tuple((
opt(alt((char('+'), char('-')))),
digit1,
opt(tuple((char('.'), digit1)))
alt((float, hexadecimal, decimal))
)))(input)?;
Ok((input, Exp::Num(Number::from(num))))
}

View File

@ -19,7 +19,14 @@ of the Shell.</p>
<ul>
<li>Basics: <code>bool</code>, <code>list</code>, <code>symbol</code>, <code>string</code></li>
<li>Numbers: <code>float</code>, <code>int</code>, <code>bigint</code></li>
<li>Number: <code>float</code>, <code>int</code>, <code>bigint</code></li>
</ul>
<h3>Literals</h3>
<ul>
<li>String: <code>&quot;Hello, World!&quot;</code></li>
<li>Number: <code>2.5</code>, <code>-25</code>, <code>255</code>, <code>0xFF</code>, <code>0xDEAD_C0DE</code></li>
</ul>
<h3>Built-in Operators</h3>
@ -244,5 +251,9 @@ language and reading from the filesystem.</p>
</ul>
<h3>Unreleased</h3>
<ul>
<li>Add hexadecimal number literals</li>
</ul>
</body>
</html>