Add array concat operator
This commit is contained in:
@@ -133,6 +133,7 @@ pub enum BinaryOp {
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Concat,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Greater,
|
||||
|
||||
@@ -410,6 +410,7 @@ impl<L: SourceLoader> Engine<L> {
|
||||
BinaryOp::Div => {
|
||||
arithmetic(lhs_value, rhs_value, ArithmeticOp::Div, span)
|
||||
}
|
||||
BinaryOp::Concat => concat_arrays(lhs_value, rhs_value, span),
|
||||
BinaryOp::Equal => {
|
||||
compare_expr(lhs_value, rhs_value, CompareExprOp::Equal, span)
|
||||
}
|
||||
@@ -1208,6 +1209,23 @@ fn comparison_type_error(span: Span) -> Diagnostic {
|
||||
)
|
||||
}
|
||||
|
||||
fn concat_arrays(lhs: RuntimeValue, rhs: RuntimeValue, span: Span) -> Result<RuntimeValue> {
|
||||
match (lhs, rhs) {
|
||||
(
|
||||
RuntimeValue::Concrete(ConcreteValue::Array(mut lhs)),
|
||||
RuntimeValue::Concrete(ConcreteValue::Array(rhs)),
|
||||
) => {
|
||||
lhs.extend(rhs);
|
||||
Ok(RuntimeValue::Concrete(ConcreteValue::Array(lhs)))
|
||||
}
|
||||
_ => Err(Diagnostic::new(
|
||||
DiagnosticKind::TypeMismatch,
|
||||
span,
|
||||
"'++' expects array values",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn arithmetic(
|
||||
lhs: RuntimeValue,
|
||||
rhs: RuntimeValue,
|
||||
@@ -1395,6 +1413,32 @@ mod tests {
|
||||
assert!(engine.eval_root().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_array_concat() {
|
||||
let data = eval_data(
|
||||
r#"
|
||||
[1, 2] ++ [3, 4]
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
data,
|
||||
Data::Array(vec![Data::Int(1), Data::Int(2), Data::Int(3), Data::Int(4)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_concat_has_lower_precedence_than_arithmetic() {
|
||||
let data = eval_data("[1 + 1] ++ [2 * 2]");
|
||||
assert_eq!(data, Data::Array(vec![Data::Int(2), Data::Int(4)]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_array_concat_operands() {
|
||||
let parsed = parse_source("[1] ++ 2").unwrap();
|
||||
let mut engine = Engine::from_parse(parsed.ast, parsed.root);
|
||||
assert!(engine.eval_root().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_logical_and_comparison_expressions() {
|
||||
let data = eval_data(
|
||||
|
||||
@@ -42,6 +42,7 @@ pub enum TokenKind {
|
||||
AmpAmp,
|
||||
PipePipe,
|
||||
Plus,
|
||||
PlusPlus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
@@ -166,7 +167,11 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
b'+' => {
|
||||
self.pos += 1;
|
||||
TokenKind::Plus
|
||||
if self.consume(b'+') {
|
||||
TokenKind::PlusPlus
|
||||
} else {
|
||||
TokenKind::Plus
|
||||
}
|
||||
}
|
||||
b'-' => {
|
||||
self.pos += 1;
|
||||
|
||||
@@ -147,6 +147,14 @@ impl Parser {
|
||||
},
|
||||
span,
|
||||
),
|
||||
InfixKind::Concat => self.ast.push(
|
||||
Expr::Binary {
|
||||
op: BinaryOp::Concat,
|
||||
lhs,
|
||||
rhs,
|
||||
},
|
||||
span,
|
||||
),
|
||||
InfixKind::Equal => self.ast.push(
|
||||
Expr::Binary {
|
||||
op: BinaryOp::Equal,
|
||||
@@ -532,6 +540,7 @@ impl Parser {
|
||||
TokenKind::Gte => Some((InfixKind::GreaterEqual, 11, 12)),
|
||||
TokenKind::Lt => Some((InfixKind::Less, 11, 12)),
|
||||
TokenKind::Lte => Some((InfixKind::LessEqual, 11, 12)),
|
||||
TokenKind::PlusPlus => Some((InfixKind::Concat, 12, 13)),
|
||||
TokenKind::Plus => Some((InfixKind::Add, 13, 14)),
|
||||
TokenKind::Minus => Some((InfixKind::Sub, 13, 14)),
|
||||
TokenKind::Star => Some((InfixKind::Mul, 15, 16)),
|
||||
@@ -632,6 +641,7 @@ enum InfixKind {
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Concat,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Greater,
|
||||
|
||||
Reference in New Issue
Block a user