dust/src/lib.rs

453 lines
13 KiB
Rust
Raw Normal View History

2024-02-23 13:23:35 +00:00
use std::{
collections::BTreeMap,
fmt::{self, Display, Formatter},
ops::Range,
};
2024-02-23 12:40:01 +00:00
2024-02-25 00:37:04 +00:00
use chumsky::{pratt::*, prelude::*, Parser};
2024-02-23 12:40:01 +00:00
2024-02-23 17:14:15 +00:00
#[derive(Clone, Debug, PartialEq)]
pub enum Statement {
Assignment(Box<Assignment>),
Expression(Expression),
Sequence(Vec<Statement>),
}
impl Statement {
pub fn value(value: Value) -> Statement {
Statement::Expression(Expression::Value(value))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Assignment {
identifier: Identifier,
statement: Statement,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expression {
Logic(Box<Logic>),
Value(Value),
}
#[derive(Clone, Debug, PartialEq)]
2024-02-25 00:37:04 +00:00
pub enum Logic {
Equal(Expression, Expression),
NotEqual(Expression, Expression),
Greater(Expression, Expression),
Less(Expression, Expression),
GreaterOrEqual(Expression, Expression),
LessOrEqual(Expression, Expression),
And(Expression, Expression),
Or(Expression, Expression),
2024-02-23 17:14:15 +00:00
}
2024-02-23 12:40:01 +00:00
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Boolean(bool),
2024-02-23 13:23:35 +00:00
Float(f64),
2024-02-23 12:40:01 +00:00
Integer(i64),
2024-02-23 13:23:35 +00:00
List(Vec<Value>),
Map(BTreeMap<Identifier, Value>),
Range(Range<i64>),
2024-02-23 12:40:01 +00:00
String(String),
}
2024-02-23 13:23:35 +00:00
#[derive(Clone, Debug, PartialEq)]
pub struct Identifier(String);
2024-02-23 17:14:15 +00:00
impl Identifier {
pub fn new(text: impl ToString) -> Self {
Identifier(text.to_string())
}
}
2024-02-23 12:40:01 +00:00
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Value::Boolean(boolean) => write!(f, "{boolean}"),
2024-02-23 13:23:35 +00:00
Value::Float(float) => write!(f, "{float}"),
2024-02-23 12:40:01 +00:00
Value::Integer(integer) => write!(f, "{integer}"),
2024-02-23 13:23:35 +00:00
Value::List(_list) => todo!(),
Value::Map(_map) => todo!(),
Value::Range(range) => write!(f, "{}..{}", range.start, range.end),
2024-02-23 12:40:01 +00:00
Value::String(string) => write!(f, "{string}"),
}
}
2023-09-28 19:58:01 +00:00
}
2024-02-25 00:37:04 +00:00
pub fn parser<'src>() -> impl Parser<'src, &'src str, Expression> {
let operator = |text| just(text).padded();
2024-02-23 17:14:15 +00:00
let value = recursive(|value| {
let boolean = just("true")
.or(just("false"))
.map(|s: &str| Value::Boolean(s.parse().unwrap()));
let float_numeric = just('-')
.or_not()
.then(text::int(10))
.then(just('.').then(text::digits(10)))
.to_slice()
.map(|text: &str| Value::Float(text.parse().unwrap()));
let float_other = choice((just("Infinity"), just("-Infinity"), just("NaN")))
.map(|text| Value::Float(text.parse().unwrap()));
let float = choice((float_numeric, float_other));
2024-02-25 00:37:04 +00:00
let integer = just('-')
.or_not()
.then(text::int(10).padded())
.to_slice()
.map(|text: &str| {
let integer = text.parse::<i64>().unwrap();
2024-02-23 17:14:15 +00:00
2024-02-25 00:37:04 +00:00
Value::Integer(integer)
});
2024-02-23 17:14:15 +00:00
let delimited_string = |delimiter| {
just(delimiter)
.ignore_then(none_of(delimiter).repeated())
.then_ignore(just(delimiter))
.to_slice()
2024-02-25 00:37:04 +00:00
.map(|text: &str| Value::String(text[1..text.len() - 1].to_string()))
2024-02-23 17:14:15 +00:00
};
let string = choice((
delimited_string('\''),
delimited_string('"'),
delimited_string('`'),
));
let list = value
.clone()
.separated_by(just(',').padded())
.allow_trailing()
.collect()
.padded()
.delimited_by(just('['), just(']'))
.map(|values| Value::List(values));
choice((boolean, float, integer, string, list))
});
2024-02-25 00:37:04 +00:00
let value_expression = value.map(|value| Expression::Value(value));
let logic_expression = value_expression.pratt((
infix(left(1), operator("=="), |left, right| {
Expression::Logic(Box::new(Logic::Equal(left, right)))
}),
infix(left(1), operator("!="), |left, right| {
Expression::Logic(Box::new(Logic::NotEqual(left, right)))
}),
infix(left(1), operator(">"), |left, right| {
Expression::Logic(Box::new(Logic::Greater(left, right)))
}),
infix(left(1), operator("<"), |left, right| {
Expression::Logic(Box::new(Logic::Less(left, right)))
}),
infix(left(1), operator(">="), |left, right| {
Expression::Logic(Box::new(Logic::GreaterOrEqual(left, right)))
}),
infix(left(1), operator("<="), |left, right| {
Expression::Logic(Box::new(Logic::LessOrEqual(left, right)))
}),
infix(left(1), operator("&&"), |left, right| {
Expression::Logic(Box::new(Logic::And(left, right)))
}),
infix(left(1), operator("||"), |left, right| {
Expression::Logic(Box::new(Logic::Or(left, right)))
}),
));
logic_expression.then_ignore(end())
2023-09-28 19:58:01 +00:00
}
#[cfg(test)]
mod tests {
2024-02-23 12:40:01 +00:00
use super::*;
2024-02-23 17:14:15 +00:00
#[test]
fn parse_list() {
assert_eq!(
parser().parse("[]").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::List(vec![]))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("[42]").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::List(vec![Value::Integer(42)]))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("[42, 'foo', \"bar\", [1, 2, 3,]]").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::List(vec![
2024-02-23 17:14:15 +00:00
Value::Integer(42),
Value::String("foo".to_string()),
Value::String("bar".to_string()),
Value::List(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
])
]))
);
}
2024-02-23 12:40:01 +00:00
#[test]
fn parse_true() {
2024-02-23 17:14:15 +00:00
assert_eq!(
parser().parse("true").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Boolean(true))
2024-02-23 17:14:15 +00:00
);
2024-02-23 12:40:01 +00:00
}
2023-09-28 19:58:01 +00:00
#[test]
2024-02-23 12:40:01 +00:00
fn parse_false() {
2024-02-23 17:14:15 +00:00
assert_eq!(
parser().parse("false").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Boolean(false))
2024-02-23 17:14:15 +00:00
);
2024-02-23 13:23:35 +00:00
}
#[test]
fn parse_positive_float() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("0.0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(0.0))
2024-02-23 13:23:35 +00:00
);
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("42.0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(42.0))
2024-02-23 17:14:15 +00:00
);
let max_float = f64::MAX.to_string() + ".0";
assert_eq!(
parser().parse(&max_float).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(f64::MAX))
2024-02-23 17:14:15 +00:00
);
let min_positive_float = f64::MIN_POSITIVE.to_string();
assert_eq!(
parser().parse(&min_positive_float).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(f64::MIN_POSITIVE))
2024-02-23 13:23:35 +00:00
);
}
#[test]
fn parse_negative_float() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("-0.0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(-0.0))
2024-02-23 13:23:35 +00:00
);
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("-42.0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(-42.0))
2024-02-23 17:14:15 +00:00
);
let min_float = f64::MIN.to_string() + ".0";
assert_eq!(
parser().parse(&min_float).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(f64::MIN))
2024-02-23 17:14:15 +00:00
);
2024-02-25 00:37:04 +00:00
let max_negative_float = format!("-{}", f64::MIN_POSITIVE);
2024-02-23 17:14:15 +00:00
assert_eq!(
parser().parse(&max_negative_float).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(-f64::MIN_POSITIVE))
2024-02-23 13:23:35 +00:00
);
}
#[test]
fn parse_other_float() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("Infinity").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(f64::INFINITY))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-Infinity").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Float(f64::NEG_INFINITY))
2024-02-23 13:23:35 +00:00
);
2024-02-25 00:37:04 +00:00
if let Expression::Value(Value::Float(float)) = parser().parse("NaN").unwrap() {
2024-02-23 13:23:35 +00:00
assert!(float.is_nan())
} else {
panic!("Expected a float.")
}
2024-02-23 12:40:01 +00:00
}
#[test]
fn parse_positive_integer() {
2024-02-23 17:14:15 +00:00
assert_eq!(
parser().parse("0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(0))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("1").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(1))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("2").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(2))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("3").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(3))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("4").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(4))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("5").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(5))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("6").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(6))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("7").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(7))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("8").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(8))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("9").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(9))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("42").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(42))
2024-02-23 17:14:15 +00:00
);
let maximum_integer = i64::MAX.to_string();
assert_eq!(
parser().parse(&maximum_integer).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(i64::MAX))
2024-02-23 12:40:01 +00:00
);
}
#[test]
fn parse_negative_integer() {
2024-02-23 17:14:15 +00:00
assert_eq!(
parser().parse("-0").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-0))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-1").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-1))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-2").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-2))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-3").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-3))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-4").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-4))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-5").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-5))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-6").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-6))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-7").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-7))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-8").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-8))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-9").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-9))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("-42").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(-42))
2024-02-23 17:14:15 +00:00
);
let minimum_integer = i64::MIN.to_string();
assert_eq!(
parser().parse(&minimum_integer).unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::Integer(i64::MIN))
2024-02-23 12:40:01 +00:00
);
}
#[test]
fn double_quoted_string() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("\"\"").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("\"1\"").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("1".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("\"42\"").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("42".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("\"foobar\"").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("foobar".to_string()))
2024-02-23 12:40:01 +00:00
);
}
#[test]
fn single_quoted_string() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("''").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("'1'").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("1".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("'42'").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("42".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("'foobar'").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("foobar".to_string()))
2024-02-23 12:40:01 +00:00
);
}
#[test]
fn grave_quoted_string() {
assert_eq!(
2024-02-23 17:14:15 +00:00
parser().parse("``").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("`1`").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("1".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("`42`").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("42".to_string()))
2024-02-23 17:14:15 +00:00
);
assert_eq!(
parser().parse("`foobar`").unwrap(),
2024-02-25 00:37:04 +00:00
Expression::Value(Value::String("foobar".to_string()))
2024-02-23 12:40:01 +00:00
);
2023-09-28 19:58:01 +00:00
}
}