aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-10-24 23:59:39 +0200
committerMel <einebeere@gmail.com>2021-10-24 23:59:39 +0200
commit31901291f3fdb7d5e98fea37dda0064188dd0961 (patch)
treec2b5e1b5ea9f85791302bed12debba3215cfceb0
parentcede973c76e061e06c7bdaad3922be5d9305f950 (diff)
downloadrabbithole-31901291f3fdb7d5e98fea37dda0064188dd0961.tar.zst
rabbithole-31901291f3fdb7d5e98fea37dda0064188dd0961.zip
And Or expressions
-rw-r--r--grammar.ebnf2
-rw-r--r--src/interpret/walker.rs24
-rw-r--r--src/lex/lexer.rs14
-rw-r--r--src/lex/token.rs3
-rw-r--r--src/parse/ast/nodes.rs4
-rw-r--r--src/parse/parser.rs34
6 files changed, 79 insertions, 2 deletions
diff --git a/grammar.ebnf b/grammar.ebnf
index d4c3721..3e7f093 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -21,6 +21,8 @@ Expression = AssignmentExpression;
(* NOTE: Only allow IDENTIFIERs and their fields as the LValue for now. *)
AssignmentExpression = IDENTIFIER {"." IDENTIFIER} ("=" | ":=") AssignmentExpression | EqualityExpression;
+OrExpression = AndExpression { "||" AndExpression };
+AndExpression = EqualityExpression { "&&" EqualityExpression };
EqualityExpression = ComparisonExpression { ("==" | "!=") ComparisonExpression };
ComparisonExpression = TermExpression { ("<" | "<=" | ">" | ">=") TermExpression };
TermExpression = FactorExpression { ("+" | "-") FactorExpression };
diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs
index b8a2e68..471da89 100644
--- a/src/interpret/walker.rs
+++ b/src/interpret/walker.rs
@@ -69,6 +69,25 @@ impl Walker {
_ => {}
}
+ // Short-circuting operators
+ if let BinOp::And | BinOp::Or = op {
+ let left_value = self.walk_expression(left)?;
+ let is_left_true = match left_value {
+ Value::Bool(bool) => bool,
+ _ => return Err(WalkerError::WrongAndOrType),
+ };
+
+ if let BinOp::And = op {
+ if !is_left_true {
+ return Ok(Value::Bool(false));
+ }
+ } else if is_left_true {
+ return Ok(Value::Bool(true));
+ }
+
+ return self.walk_expression(right);
+ }
+
let left = self.walk_expression(left)?;
let right = self.walk_expression(right)?;
@@ -78,13 +97,14 @@ impl Walker {
BinOp::Minus => left.sub(right),
BinOp::Star => left.mul(right),
BinOp::Slash => left.div(right),
+ BinOp::Dot => todo!("Structures not implemented yet."),
BinOp::Eq => left.eq(right),
BinOp::Neq => left.neq(right),
BinOp::Gt => left.gt(right),
BinOp::Gte => left.gte(right),
BinOp::Lt => right.gt(left),
BinOp::Lte => right.gte(left),
- BinOp::Dot => todo!("Structures not implemented yet."),
+
_ => unreachable!(),
}
.map_err(WalkerError::OperationError)
@@ -245,6 +265,8 @@ pub enum WalkerError {
WrongLoopConditionType,
#[error("If and Elif expressions can only take boolean values as conditions.")]
WrongIfConditionType,
+ #[error("&& and || expressions can only take boolean values as their operands.")]
+ WrongAndOrType,
#[error("Can only assign to identifiers and member or array access results.")]
NonLValueAssignment,
#[error(transparent)]
diff --git a/src/lex/lexer.rs b/src/lex/lexer.rs
index edd1ff0..1d46ce7 100644
--- a/src/lex/lexer.rs
+++ b/src/lex/lexer.rs
@@ -79,6 +79,20 @@ impl Iterator for Lexer<'_> {
OpGt
}
}
+ '&' => {
+ if self.advance_if('&') {
+ OpAnd
+ } else {
+ Unknown('&')
+ }
+ }
+ '|' => {
+ if self.advance_if('|') {
+ OpOr
+ } else {
+ Unknown('|')
+ }
+ }
'(' => GroupOpen,
')' => GroupClose,
'{' => BlockOpen,
diff --git a/src/lex/token.rs b/src/lex/token.rs
index 3c6d5c6..77b0df6 100644
--- a/src/lex/token.rs
+++ b/src/lex/token.rs
@@ -25,6 +25,9 @@ pub enum TokenVariant {
OpGt,
OpLte,
OpGte,
+ // Short-circuting boolean operators
+ OpAnd,
+ OpOr,
// Statement symbols
Dot,
diff --git a/src/parse/ast/nodes.rs b/src/parse/ast/nodes.rs
index bffaea3..2bf3f17 100644
--- a/src/parse/ast/nodes.rs
+++ b/src/parse/ast/nodes.rs
@@ -14,6 +14,8 @@ pub enum BinaryOperator {
Gte,
Lt,
Lte,
+ And,
+ Or,
Assign,
ConstAssign,
Dot,
@@ -32,6 +34,8 @@ impl BinaryOperator {
OpGt => Self::Gt,
OpLte => Self::Lte,
OpGte => Self::Gte,
+ OpAnd => Self::And,
+ OpOr => Self::Or,
Assign => Self::Assign,
ConstAssign => Self::ConstAssign,
Dot => Self::Dot,
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index a882f35..6b0a785 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -87,7 +87,7 @@ impl<T: Iterator<Item = Token>> Parser<T> {
fn assignment_expression(&mut self) -> Result<Expression> {
// Parse any expressions as l-values for now.
- let left = self.equality_expression()?;
+ let left = self.or_expression()?;
if let Some(op) = consume_if!(self, Assign | ConstAssign) {
let right = self.assignment_expression()?;
@@ -102,6 +102,38 @@ impl<T: Iterator<Item = Token>> Parser<T> {
}
}
+ fn or_expression(&mut self) -> Result<Expression> {
+ let mut left = self.and_expression()?;
+
+ while let Some(op) = consume_if!(self, OpAnd) {
+ let right = self.and_expression()?;
+
+ left = Expression::Binary {
+ left: Box::new(left),
+ op: BinaryOperator::from_token(op),
+ right: Box::new(right),
+ };
+ }
+
+ Ok(left)
+ }
+
+ fn and_expression(&mut self) -> Result<Expression> {
+ let mut left = self.equality_expression()?;
+
+ while let Some(op) = consume_if!(self, OpOr) {
+ let right = self.equality_expression()?;
+
+ left = Expression::Binary {
+ left: Box::new(left),
+ op: BinaryOperator::from_token(op),
+ right: Box::new(right),
+ };
+ }
+
+ Ok(left)
+ }
+
fn equality_expression(&mut self) -> Result<Expression> {
let mut left = self.comparison_expression()?;