Add plumbing

This commit is contained in:
Jeff 2024-02-25 03:12:09 -05:00
parent 989afec531
commit f70656c837

View File

@ -1,24 +1,168 @@
use std::{collections::BTreeMap, ops::Range}; use std::{
collections::BTreeMap,
ops::Range,
sync::{Arc, OnceLock, PoisonError, RwLock},
};
use chumsky::{prelude::*, Parser}; use chumsky::{prelude::*, Parser};
pub static NONE: OnceLock<Value> = OnceLock::new();
pub enum BuiltInValue {
None,
}
impl BuiltInValue {
pub fn get(&self) -> Value {
match self {
BuiltInValue::None => NONE.get_or_init(|| {
Value::r#enum(EnumInstance {
type_name: Identifier("Option".to_string()),
variant: Identifier("None".to_string()),
})
}),
}
.clone()
}
}
pub enum Error<'src> {
Parse(Vec<Rich<'src, char>>),
Runtime(RuntimeError),
}
impl<'src> From<Vec<Rich<'src, char>>> for Error<'src> {
fn from(errors: Vec<Rich<'src, char>>) -> Self {
Error::Parse(errors)
}
}
impl<'src> From<RuntimeError> for Error<'src> {
fn from(error: RuntimeError) -> Self {
Error::Runtime(error)
}
}
pub enum RuntimeError {
RwLockPoison(RwLockPoisonError),
}
impl From<RwLockPoisonError> for RuntimeError {
fn from(error: RwLockPoisonError) -> Self {
RuntimeError::RwLockPoison(error)
}
}
pub struct RwLockPoisonError;
impl<T> From<PoisonError<T>> for RwLockPoisonError {
fn from(_: PoisonError<T>) -> Self {
RwLockPoisonError
}
}
pub struct Context {
inner: Arc<RwLock<BTreeMap<Identifier, Value>>>,
}
impl Context {
pub fn get(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockPoisonError> {
let value = self.inner.read()?.get(&identifier).cloned();
Ok(value)
}
pub fn set(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> {
self.inner.write()?.insert(identifier, value);
Ok(())
}
}
pub trait AbstractTree {
fn run(self, context: &Context) -> Result<Value, RuntimeError>;
}
pub struct Interpreter<P> {
parser: P,
context: Context,
}
impl<'src, P> Interpreter<P>
where
P: Parser<'src, &'src str, Statement, extra::Err<Rich<'src, char>>>,
{
pub fn run(&self, source: &'src str) -> Result<Value, Error<'src>> {
let final_value = self
.parser
.parse(source)
.into_result()?
.run(&self.context)?;
Ok(final_value)
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Statement { pub enum Statement {
Assignment(Assignment), Assignment(Assignment),
Expression(Expression),
}
impl AbstractTree for Statement {
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
match self {
Statement::Assignment(assignment) => assignment.run(context),
Statement::Expression(expression) => expression.run(context),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expression {
Identifier(Identifier), Identifier(Identifier),
Logic(Logic), Logic(Logic),
Value(Value), Value(Value),
} }
#[derive(Clone, Debug, PartialEq)] impl AbstractTree for Expression {
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
match self {
Expression::Identifier(identifier) => identifier.run(context),
Expression::Logic(logic) => logic.run(context),
Expression::Value(value) => value.run(context),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Identifier(String); pub struct Identifier(String);
impl AbstractTree for Identifier {
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
let value = context
.get(&self)?
.unwrap_or_else(|| BuiltInValue::None.get())
.clone();
Ok(value)
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Assignment { pub struct Assignment {
identifier: Identifier, identifier: Identifier,
value: Value, value: Value,
} }
impl AbstractTree for Assignment {
fn run(self, context: &Context) -> Result<Value, RuntimeError> {
context.set(self.identifier, self.value)?;
Ok(BuiltInValue::None.get().clone())
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Logic { pub struct Logic {
left: LogicExpression, left: LogicExpression,
@ -26,9 +170,22 @@ pub struct Logic {
right: LogicExpression, right: LogicExpression,
} }
impl AbstractTree for Logic {
fn run(self, _: &Context) -> Result<Value, RuntimeError> {
todo!()
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum LogicOperator { pub enum LogicOperator {
Equal, Equal,
NotEqual,
Greater,
Less,
GreaterOrEqual,
LessOrEqual,
And,
Or,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -39,14 +196,64 @@ pub enum LogicExpression {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Value { pub struct Value(Arc<ValueInner>);
impl Value {
pub fn boolean(boolean: bool) -> Self {
Value(Arc::new(ValueInner::Boolean(boolean)))
}
pub fn float(float: f64) -> Self {
Value(Arc::new(ValueInner::Float(float)))
}
pub fn integer(integer: i64) -> Self {
Value(Arc::new(ValueInner::Integer(integer)))
}
pub fn list(list: Vec<Value>) -> Self {
Value(Arc::new(ValueInner::List(list)))
}
pub fn map(map: BTreeMap<Identifier, Value>) -> Self {
Value(Arc::new(ValueInner::Map(map)))
}
pub fn range(range: Range<i64>) -> Self {
Value(Arc::new(ValueInner::Range(range)))
}
pub fn string(string: String) -> Self {
Value(Arc::new(ValueInner::String(string)))
}
pub fn r#enum(r#enum: EnumInstance) -> Self {
Value(Arc::new(ValueInner::Enum(r#enum)))
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ValueInner {
Boolean(bool), Boolean(bool),
Float(f64), Float(f64),
Integer(i64), Integer(i64),
List(Vec<Value>), List(Vec<Value>),
Map(BTreeMap<String, Value>), Map(BTreeMap<Identifier, Value>),
Range(Range<i64>), Range(Range<i64>),
String(String), String(String),
Enum(EnumInstance),
}
#[derive(Clone, Debug, PartialEq)]
pub struct EnumInstance {
type_name: Identifier,
variant: Identifier,
}
impl AbstractTree for Value {
fn run(self, _: &Context) -> Result<Value, RuntimeError> {
Ok(self)
}
} }
pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich<'src, char>>> { pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich<'src, char>>> {
@ -55,17 +262,17 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
let value = recursive(|value| { let value = recursive(|value| {
let boolean = just("true") let boolean = just("true")
.or(just("false")) .or(just("false"))
.map(|s: &str| Value::Boolean(s.parse().unwrap())); .map(|s: &str| Value::boolean(s.parse().unwrap()));
let float_numeric = just('-') let float_numeric = just('-')
.or_not() .or_not()
.then(text::int(10)) .then(text::int(10))
.then(just('.').then(text::digits(10))) .then(just('.').then(text::digits(10)))
.to_slice() .to_slice()
.map(|text: &str| Value::Float(text.parse().unwrap())); .map(|text: &str| Value::float(text.parse().unwrap()));
let float_other = choice((just("Infinity"), just("-Infinity"), just("NaN"))) let float_other = choice((just("Infinity"), just("-Infinity"), just("NaN")))
.map(|text| Value::Float(text.parse().unwrap())); .map(|text| Value::float(text.parse().unwrap()));
let float = choice((float_numeric, float_other)); let float = choice((float_numeric, float_other));
@ -76,7 +283,7 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
.map(|text: &str| { .map(|text: &str| {
let integer = text.parse::<i64>().unwrap(); let integer = text.parse::<i64>().unwrap();
Value::Integer(integer) Value::integer(integer)
}); });
let delimited_string = |delimiter| { let delimited_string = |delimiter| {
@ -84,7 +291,7 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
.ignore_then(none_of(delimiter).repeated()) .ignore_then(none_of(delimiter).repeated())
.then_ignore(just(delimiter)) .then_ignore(just(delimiter))
.to_slice() .to_slice()
.map(|text: &str| Value::String(text[1..text.len() - 1].to_string())) .map(|text: &str| Value::string(text[1..text.len() - 1].to_string()))
}; };
let string = choice(( let string = choice((
@ -100,7 +307,7 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
.collect() .collect()
.padded() .padded()
.delimited_by(just('['), just(']')) .delimited_by(just('['), just(']'))
.map(|values| Value::List(values)); .map(|values| Value::list(values));
choice((boolean, float, integer, string, list)) choice((boolean, float, integer, string, list))
}); });
@ -120,7 +327,16 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
.clone() .clone()
.map(|logic| LogicExpression::Logic(Box::new(logic))), .map(|logic| LogicExpression::Logic(Box::new(logic))),
)) ))
.then(operator("==").map(|_| LogicOperator::Equal)) .then(choice((
operator("==").map(|_| LogicOperator::Equal),
operator("!=").map(|_| LogicOperator::NotEqual),
operator(">").map(|_| LogicOperator::Greater),
operator("<").map(|_| LogicOperator::Less),
operator(">=").map(|_| LogicOperator::GreaterOrEqual),
operator("<=").map(|_| LogicOperator::LessOrEqual),
operator("&&").map(|_| LogicOperator::And),
operator("||").map(|_| LogicOperator::Or),
)))
.then(choice(( .then(choice((
value.clone().map(|value| LogicExpression::Value(value)), value.clone().map(|value| LogicExpression::Value(value)),
identifier.map(|identifier| LogicExpression::Identifier(identifier)), identifier.map(|identifier| LogicExpression::Identifier(identifier)),
@ -133,12 +349,18 @@ pub fn parser<'src>() -> impl Parser<'src, &'src str, Statement, extra::Err<Rich
}) })
}); });
choice(( let expression = choice((
logic.map(|logic| Statement::Logic(logic)), logic.map(|logic| Expression::Logic(logic)),
value.map(|value| Expression::Value(value)),
identifier.map(|identifier| Expression::Identifier(identifier)),
));
let statement = choice((
assignment.map(|assignment| Statement::Assignment(assignment)), assignment.map(|assignment| Statement::Assignment(assignment)),
value.map(|value| Statement::Value(value)), expression.map(|expression| Statement::Expression(expression)),
identifier.map(|identifier| Statement::Identifier(identifier)), ));
))
statement
} }
#[cfg(test)] #[cfg(test)]
@ -149,25 +371,25 @@ mod tests {
fn parse_identifier() { fn parse_identifier() {
assert_eq!( assert_eq!(
parser().parse("x").unwrap(), parser().parse("x").unwrap(),
Statement::Identifier(Identifier("x".to_string())) Statement::Expression(Expression::Identifier(Identifier("x".to_string())))
); );
assert_eq!( assert_eq!(
parser().parse("foobar").unwrap(), parser().parse("foobar").unwrap(),
Statement::Identifier(Identifier("foobar".to_string())), Statement::Expression(Expression::Identifier(Identifier("foobar".to_string())))
); );
assert_eq!( assert_eq!(
parser().parse("HELLO").unwrap(), parser().parse("HELLO").unwrap(),
Statement::Identifier(Identifier("HELLO".to_string())), Statement::Expression(Expression::Identifier(Identifier("HELLO".to_string())))
); );
} }
#[test] #[test]
fn parse_assignment() { fn parse_assignment() {
assert_eq!( assert_eq!(
parser().parse("foobar=1").unwrap(), parser().parse("foobar = 1").unwrap(),
Statement::Assignment(Assignment { Statement::Assignment(Assignment {
identifier: Identifier("foobar".to_string()), identifier: Identifier("foobar".to_string()),
value: Value::Integer(1) value: Value::integer(1)
}) })
); );
} }
@ -176,11 +398,11 @@ mod tests {
fn parse_logic() { fn parse_logic() {
assert_eq!( assert_eq!(
parser().parse("x == 1").unwrap(), parser().parse("x == 1").unwrap(),
Statement::Logic(Logic { Statement::Expression(Expression::Logic(Logic {
left: LogicExpression::Identifier(Identifier("x".to_string())), left: LogicExpression::Identifier(Identifier("x".to_string())),
operator: LogicOperator::Equal, operator: LogicOperator::Equal,
right: LogicExpression::Value(Value::Integer(1)) right: LogicExpression::Value(Value::integer(1))
}) }))
); );
} }
@ -188,24 +410,24 @@ mod tests {
fn parse_list() { fn parse_list() {
assert_eq!( assert_eq!(
parser().parse("[]").unwrap(), parser().parse("[]").unwrap(),
Statement::Value(Value::List(vec![])) Statement::Expression(Expression::Value(Value::list(vec![])))
); );
assert_eq!( assert_eq!(
parser().parse("[42]").unwrap(), parser().parse("[42]").unwrap(),
Statement::Value(Value::List(vec![Value::Integer(42)])) Statement::Expression(Expression::Value(Value::list(vec![Value::integer(42)])))
); );
assert_eq!( assert_eq!(
parser().parse("[42, 'foo', \"bar\", [1, 2, 3,]]").unwrap(), parser().parse("[42, 'foo', \"bar\", [1, 2, 3,]]").unwrap(),
Statement::Value(Value::List(vec![ Statement::Expression(Expression::Value(Value::list(vec![
Value::Integer(42), Value::integer(42),
Value::String("foo".to_string()), Value::string("foo".to_string()),
Value::String("bar".to_string()), Value::string("bar".to_string()),
Value::List(vec![ Value::list(vec![
Value::Integer(1), Value::integer(1),
Value::Integer(2), Value::integer(2),
Value::Integer(3), Value::integer(3),
]) ])
])) ])))
); );
} }
@ -213,7 +435,7 @@ mod tests {
fn parse_true() { fn parse_true() {
assert_eq!( assert_eq!(
parser().parse("true").unwrap(), parser().parse("true").unwrap(),
Statement::Value(Value::Boolean(true)) Statement::Expression(Expression::Value(Value::boolean(true)))
); );
} }
@ -221,7 +443,7 @@ mod tests {
fn parse_false() { fn parse_false() {
assert_eq!( assert_eq!(
parser().parse("false").unwrap(), parser().parse("false").unwrap(),
Statement::Value(Value::Boolean(false)) Statement::Expression(Expression::Value(Value::boolean(false)))
); );
} }
@ -229,25 +451,25 @@ mod tests {
fn parse_positive_float() { fn parse_positive_float() {
assert_eq!( assert_eq!(
parser().parse("0.0").unwrap(), parser().parse("0.0").unwrap(),
Statement::Value(Value::Float(0.0)) Statement::Expression(Expression::Value(Value::float(0.0)))
); );
assert_eq!( assert_eq!(
parser().parse("42.0").unwrap(), parser().parse("42.0").unwrap(),
Statement::Value(Value::Float(42.0)) Statement::Expression(Expression::Value(Value::float(42.0)))
); );
let max_float = f64::MAX.to_string() + ".0"; let max_float = f64::MAX.to_string() + ".0";
assert_eq!( assert_eq!(
parser().parse(&max_float).unwrap(), parser().parse(&max_float).unwrap(),
Statement::Value(Value::Float(f64::MAX)) Statement::Expression(Expression::Value(Value::float(f64::MAX)))
); );
let min_positive_float = f64::MIN_POSITIVE.to_string(); let min_positive_float = f64::MIN_POSITIVE.to_string();
assert_eq!( assert_eq!(
parser().parse(&min_positive_float).unwrap(), parser().parse(&min_positive_float).unwrap(),
Statement::Value(Value::Float(f64::MIN_POSITIVE)) Statement::Expression(Expression::Value(Value::float(f64::MIN_POSITIVE)))
); );
} }
@ -255,25 +477,25 @@ mod tests {
fn parse_negative_float() { fn parse_negative_float() {
assert_eq!( assert_eq!(
parser().parse("-0.0").unwrap(), parser().parse("-0.0").unwrap(),
Statement::Value(Value::Float(-0.0)) Statement::Expression(Expression::Value(Value::float(-0.0)))
); );
assert_eq!( assert_eq!(
parser().parse("-42.0").unwrap(), parser().parse("-42.0").unwrap(),
Statement::Value(Value::Float(-42.0)) Statement::Expression(Expression::Value(Value::float(-42.0)))
); );
let min_float = f64::MIN.to_string() + ".0"; let min_float = f64::MIN.to_string() + ".0";
assert_eq!( assert_eq!(
parser().parse(&min_float).unwrap(), parser().parse(&min_float).unwrap(),
Statement::Value(Value::Float(f64::MIN)) Statement::Expression(Expression::Value(Value::float(f64::MIN)))
); );
let max_negative_float = format!("-{}", f64::MIN_POSITIVE); let max_negative_float = format!("-{}", f64::MIN_POSITIVE);
assert_eq!( assert_eq!(
parser().parse(&max_negative_float).unwrap(), parser().parse(&max_negative_float).unwrap(),
Statement::Value(Value::Float(-f64::MIN_POSITIVE)) Statement::Expression(Expression::Value(Value::float(-f64::MIN_POSITIVE)))
); );
} }
@ -281,127 +503,71 @@ mod tests {
fn parse_other_float() { fn parse_other_float() {
assert_eq!( assert_eq!(
parser().parse("Infinity").unwrap(), parser().parse("Infinity").unwrap(),
Statement::Value(Value::Float(f64::INFINITY)) Statement::Expression(Expression::Value(Value::float(f64::INFINITY)))
); );
assert_eq!( assert_eq!(
parser().parse("-Infinity").unwrap(), parser().parse("-Infinity").unwrap(),
Statement::Value(Value::Float(f64::NEG_INFINITY)) Statement::Expression(Expression::Value(Value::float(f64::NEG_INFINITY)))
); );
if let Statement::Value(Value::Float(float)) = parser().parse("NaN").unwrap() { if let Statement::Expression(Expression::Value(Value(value_inner))) =
assert!(float.is_nan()) parser().parse("NaN").unwrap()
} else { {
panic!("Expected a float.") if let ValueInner::Float(float) = value_inner.as_ref() {
return assert!(float.is_nan());
}
} }
panic!("Expected a float.")
} }
#[test] #[test]
fn parse_positive_integer() { fn parse_positive_integer() {
assert_eq!( for i in 0..10 {
parser().parse("0").unwrap(), let source = i.to_string();
Statement::Value(Value::Integer(0)) let result = parser().parse(&source);
);
assert_eq!( assert_eq!(
parser().parse("1").unwrap(), result.unwrap(),
Statement::Value(Value::Integer(1)) Statement::Expression(Expression::Value(Value::integer(i)))
); )
assert_eq!( }
parser().parse("2").unwrap(),
Statement::Value(Value::Integer(2))
);
assert_eq!(
parser().parse("3").unwrap(),
Statement::Value(Value::Integer(3))
);
assert_eq!(
parser().parse("4").unwrap(),
Statement::Value(Value::Integer(4))
);
assert_eq!(
parser().parse("5").unwrap(),
Statement::Value(Value::Integer(5))
);
assert_eq!(
parser().parse("6").unwrap(),
Statement::Value(Value::Integer(6))
);
assert_eq!(
parser().parse("7").unwrap(),
Statement::Value(Value::Integer(7))
);
assert_eq!(
parser().parse("8").unwrap(),
Statement::Value(Value::Integer(8))
);
assert_eq!(
parser().parse("9").unwrap(),
Statement::Value(Value::Integer(9))
);
assert_eq!( assert_eq!(
parser().parse("42").unwrap(), parser().parse("42").unwrap(),
Statement::Value(Value::Integer(42)) Statement::Expression(Expression::Value(Value::integer(42)))
); );
let maximum_integer = i64::MAX.to_string(); let maximum_integer = i64::MAX.to_string();
assert_eq!( assert_eq!(
parser().parse(&maximum_integer).unwrap(), parser().parse(&maximum_integer).unwrap(),
Statement::Value(Value::Integer(i64::MAX)) Statement::Expression(Expression::Value(Value::integer(i64::MAX)))
); );
} }
#[test] #[test]
fn parse_negative_integer() { fn parse_negative_integer() {
assert_eq!( for i in -9..1 {
parser().parse("-0").unwrap(), let source = i.to_string();
Statement::Value(Value::Integer(-0)) let result = parser().parse(&source);
);
assert_eq!( assert_eq!(
parser().parse("-1").unwrap(), result.unwrap(),
Statement::Value(Value::Integer(-1)) Statement::Expression(Expression::Value(Value::integer(i)))
); )
assert_eq!( }
parser().parse("-2").unwrap(),
Statement::Value(Value::Integer(-2))
);
assert_eq!(
parser().parse("-3").unwrap(),
Statement::Value(Value::Integer(-3))
);
assert_eq!(
parser().parse("-4").unwrap(),
Statement::Value(Value::Integer(-4))
);
assert_eq!(
parser().parse("-5").unwrap(),
Statement::Value(Value::Integer(-5))
);
assert_eq!(
parser().parse("-6").unwrap(),
Statement::Value(Value::Integer(-6))
);
assert_eq!(
parser().parse("-7").unwrap(),
Statement::Value(Value::Integer(-7))
);
assert_eq!(
parser().parse("-8").unwrap(),
Statement::Value(Value::Integer(-8))
);
assert_eq!(
parser().parse("-9").unwrap(),
Statement::Value(Value::Integer(-9))
);
assert_eq!( assert_eq!(
parser().parse("-42").unwrap(), parser().parse("-42").unwrap(),
Statement::Value(Value::Integer(-42)) Statement::Expression(Expression::Value(Value::integer(-42)))
); );
let minimum_integer = i64::MIN.to_string(); let minimum_integer = i64::MIN.to_string();
assert_eq!( assert_eq!(
parser().parse(&minimum_integer).unwrap(), parser().parse(&minimum_integer).unwrap(),
Statement::Value(Value::Integer(i64::MIN)) Statement::Expression(Expression::Value(Value::integer(i64::MIN)))
); );
} }
@ -409,19 +575,15 @@ mod tests {
fn double_quoted_string() { fn double_quoted_string() {
assert_eq!( assert_eq!(
parser().parse("\"\"").unwrap(), parser().parse("\"\"").unwrap(),
Statement::Value(Value::String("".to_string())) Statement::Expression(Expression::Value(Value::string("".to_string())))
);
assert_eq!(
parser().parse("\"1\"").unwrap(),
Statement::Value(Value::String("1".to_string()))
); );
assert_eq!( assert_eq!(
parser().parse("\"42\"").unwrap(), parser().parse("\"42\"").unwrap(),
Statement::Value(Value::String("42".to_string())) Statement::Expression(Expression::Value(Value::string("42".to_string())))
); );
assert_eq!( assert_eq!(
parser().parse("\"foobar\"").unwrap(), parser().parse("\"foobar\"").unwrap(),
Statement::Value(Value::String("foobar".to_string())) Statement::Expression(Expression::Value(Value::string("foobar".to_string())))
); );
} }
@ -429,19 +591,15 @@ mod tests {
fn single_quoted_string() { fn single_quoted_string() {
assert_eq!( assert_eq!(
parser().parse("''").unwrap(), parser().parse("''").unwrap(),
Statement::Value(Value::String("".to_string())) Statement::Expression(Expression::Value(Value::string("".to_string())))
);
assert_eq!(
parser().parse("'1'").unwrap(),
Statement::Value(Value::String("1".to_string()))
); );
assert_eq!( assert_eq!(
parser().parse("'42'").unwrap(), parser().parse("'42'").unwrap(),
Statement::Value(Value::String("42".to_string())) Statement::Expression(Expression::Value(Value::string("42".to_string())))
); );
assert_eq!( assert_eq!(
parser().parse("'foobar'").unwrap(), parser().parse("'foobar'").unwrap(),
Statement::Value(Value::String("foobar".to_string())) Statement::Expression(Expression::Value(Value::string("foobar".to_string())))
); );
} }
@ -449,19 +607,15 @@ mod tests {
fn grave_quoted_string() { fn grave_quoted_string() {
assert_eq!( assert_eq!(
parser().parse("``").unwrap(), parser().parse("``").unwrap(),
Statement::Value(Value::String("".to_string())) Statement::Expression(Expression::Value(Value::string("".to_string())))
);
assert_eq!(
parser().parse("`1`").unwrap(),
Statement::Value(Value::String("1".to_string()))
); );
assert_eq!( assert_eq!(
parser().parse("`42`").unwrap(), parser().parse("`42`").unwrap(),
Statement::Value(Value::String("42".to_string())) Statement::Expression(Expression::Value(Value::string("42".to_string())))
); );
assert_eq!( assert_eq!(
parser().parse("`foobar`").unwrap(), parser().parse("`foobar`").unwrap(),
Statement::Value(Value::String("foobar".to_string())) Statement::Expression(Expression::Value(Value::string("foobar".to_string())))
); );
} }
} }