Skip to content

Commit bce13d8

Browse files
authored
feat: arithmetic expressions (#275)
1 parent 060623b commit bce13d8

File tree

6 files changed

+412
-150
lines changed

6 files changed

+412
-150
lines changed

crates/deno_task_shell/src/grammar.pest

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -248,27 +248,33 @@ bitwise_or = { "|" }
248248
logical_and = { "&&" }
249249
logical_or = { "||" }
250250

251+
unary_plus = { "+" }
252+
unary_minus = { "-" }
253+
logical_not = { "!" }
254+
bitwise_not = { "~" }
255+
increment = { "++" }
256+
decrement = { "--" }
257+
251258
unary_arithmetic_expr = !{
252-
(unary_arithmetic_op | post_arithmetic_op) ~ (parentheses_expr | VARIABLE | NUMBER) |
253-
(parentheses_expr | VARIABLE | NUMBER) ~ post_arithmetic_op
259+
unary_pre_arithmetic_expr | unary_post_arithmetic_expr
254260
}
255261

256-
unary_arithmetic_op = _{
257-
unary_plus | unary_minus | logical_not | bitwise_not
262+
unary_pre_arithmetic_expr = !{
263+
pre_arithmetic_op ~ (parentheses_expr | VARIABLE | NUMBER)
258264
}
259265

260-
unary_plus = { "+" }
261-
unary_minus = { "-" }
262-
logical_not = { "!" }
263-
bitwise_not = { "~" }
266+
unary_post_arithmetic_expr = !{
267+
(parentheses_expr | VARIABLE | NUMBER) ~ post_arithmetic_op
268+
}
269+
270+
pre_arithmetic_op= !{
271+
increment | decrement | unary_plus | unary_minus | logical_not | bitwise_not
272+
}
264273

265274
post_arithmetic_op = !{
266275
increment | decrement
267276
}
268277

269-
increment = { "++" }
270-
decrement = { "--" }
271-
272278
assignment_operator = _{
273279
assign | multiply_assign | divide_assign | modulo_assign | add_assign | subtract_assign |
274280
left_shift_assign | right_shift_assign | bitwise_and_assign | bitwise_xor_assign | bitwise_or_assign

crates/deno_task_shell/src/parser.rs

Lines changed: 135 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,6 @@ pub enum ArithmeticPart {
495495
operator: UnaryArithmeticOp,
496496
operand: Box<ArithmeticPart>,
497497
},
498-
#[error("Invalid post arithmetic expression")]
499-
PostArithmeticExpr {
500-
operand: Box<ArithmeticPart>,
501-
operator: PostArithmeticOp,
502-
},
503498
#[error("Invalid variable")]
504499
Variable(String),
505500
#[error("Invalid number")]
@@ -545,7 +540,9 @@ pub enum AssignmentOp {
545540
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
546541
#[cfg_attr(feature = "serialization", serde(rename_all = "camelCase"))]
547542
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
548-
pub enum UnaryArithmeticOp {
543+
pub enum PreArithmeticOp {
544+
Increment, // ++
545+
Decrement, // --
549546
Plus, // +
550547
Minus, // -
551548
LogicalNot, // !
@@ -554,12 +551,20 @@ pub enum UnaryArithmeticOp {
554551

555552
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
556553
#[cfg_attr(feature = "serialization", serde(rename_all = "camelCase"))]
557-
#[derive(Debug, Clone, PartialEq, Eq)]
554+
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
558555
pub enum PostArithmeticOp {
559556
Increment, // ++
560557
Decrement, // --
561558
}
562559

560+
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
561+
#[cfg_attr(feature = "serialization", serde(rename_all = "camelCase"))]
562+
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
563+
pub enum UnaryArithmeticOp {
564+
Pre(PreArithmeticOp),
565+
Post(PostArithmeticOp),
566+
}
567+
563568
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
564569
#[cfg_attr(
565570
feature = "serialization",
@@ -1791,56 +1796,151 @@ fn parse_arithmetic_expr(pair: Pair<Rule>) -> Result<ArithmeticPart> {
17911796

17921797
fn parse_unary_arithmetic_expr(pair: Pair<Rule>) -> Result<ArithmeticPart> {
17931798
let mut inner = pair.into_inner();
1794-
let first = inner.next().unwrap();
1799+
let first = inner
1800+
.next()
1801+
.ok_or_else(|| miette!("Expected unary operator"))?;
1802+
1803+
match first.as_rule() {
1804+
Rule::unary_pre_arithmetic_expr => unary_pre_arithmetic_expr(first),
1805+
Rule::unary_post_arithmetic_expr => unary_post_arithmetic_expr(first),
1806+
_ => Err(miette!(
1807+
"Unexpected rule in unary arithmetic expression: {:?}",
1808+
first.as_rule()
1809+
)),
1810+
}
1811+
}
1812+
1813+
fn unary_pre_arithmetic_expr(pair: Pair<Rule>) -> Result<ArithmeticPart> {
1814+
let mut inner = pair.into_inner();
1815+
let first = inner
1816+
.next()
1817+
.ok_or_else(|| miette!("Expected unary pre operator"))?;
1818+
let second = inner.next().ok_or_else(|| miette!("Expected operand"))?;
1819+
let operand = match second.as_rule() {
1820+
Rule::parentheses_expr => {
1821+
let inner = second
1822+
.into_inner()
1823+
.next()
1824+
.ok_or_else(|| miette!("Expected expression in parentheses"))?;
1825+
let parts = parse_arithmetic_sequence(inner)?;
1826+
Ok(ArithmeticPart::ParenthesesExpr(Box::new(Arithmetic {
1827+
parts,
1828+
})))
1829+
}
1830+
Rule::VARIABLE => {
1831+
Ok(ArithmeticPart::Variable(second.as_str().to_string()))
1832+
}
1833+
Rule::NUMBER => Ok(ArithmeticPart::Number(second.as_str().to_string())),
1834+
_ => Err(miette!(
1835+
"Unexpected rule in arithmetic expression: {:?}",
1836+
second.as_rule()
1837+
)),
1838+
}?;
17951839

17961840
match first.as_rule() {
1797-
Rule::unary_arithmetic_op => {
1798-
let op = parse_unary_arithmetic_op(first)?;
1799-
let operand = parse_arithmetic_expr(inner.next().unwrap())?;
1841+
Rule::pre_arithmetic_op => {
1842+
let op = parse_pre_arithmetic_op(first)?;
1843+
// Validate that increment/decrement are only applied to variables or expressions
1844+
if matches!(
1845+
op,
1846+
PreArithmeticOp::Increment | PreArithmeticOp::Decrement
1847+
) && matches!(operand, ArithmeticPart::Number(_))
1848+
{
1849+
return Err(miette!(
1850+
"Increment/decrement operators cannot be applied to literal numbers"
1851+
));
1852+
}
18001853
Ok(ArithmeticPart::UnaryArithmeticExpr {
1801-
operator: op,
1854+
operator: UnaryArithmeticOp::Pre(op),
18021855
operand: Box::new(operand),
18031856
})
18041857
}
18051858
Rule::post_arithmetic_op => {
1806-
let operand = parse_arithmetic_expr(inner.next().unwrap())?;
18071859
let op = parse_post_arithmetic_op(first)?;
1808-
Ok(ArithmeticPart::PostArithmeticExpr {
1860+
Ok(ArithmeticPart::UnaryArithmeticExpr {
1861+
operator: UnaryArithmeticOp::Post(op),
18091862
operand: Box::new(operand),
1810-
operator: op,
18111863
})
18121864
}
1813-
_ => {
1814-
let operand = parse_arithmetic_expr(first)?;
1815-
let op = parse_post_arithmetic_op(inner.next().unwrap())?;
1816-
Ok(ArithmeticPart::PostArithmeticExpr {
1817-
operand: Box::new(operand),
1818-
operator: op,
1819-
})
1865+
_ => Err(miette!(
1866+
"Unexpected rule in unary arithmetic operator: {:?}",
1867+
first.as_rule()
1868+
)),
1869+
}
1870+
}
1871+
1872+
fn unary_post_arithmetic_expr(pair: Pair<Rule>) -> Result<ArithmeticPart> {
1873+
let mut inner = pair.into_inner();
1874+
let first = inner
1875+
.next()
1876+
.ok_or_else(|| miette!("Expected unary post operator"))?;
1877+
let second = inner.next().ok_or_else(|| miette!("Expected operand"))?;
1878+
1879+
let operand = match first.as_rule() {
1880+
Rule::parentheses_expr => {
1881+
let inner = first
1882+
.into_inner()
1883+
.next()
1884+
.ok_or_else(|| miette!("Expected expression in parentheses"))?;
1885+
let parts = parse_arithmetic_sequence(inner)?;
1886+
Ok(ArithmeticPart::ParenthesesExpr(Box::new(Arithmetic {
1887+
parts,
1888+
})))
1889+
}
1890+
Rule::VARIABLE => {
1891+
Ok(ArithmeticPart::Variable(first.as_str().to_string()))
18201892
}
1893+
Rule::NUMBER => Ok(ArithmeticPart::Number(first.as_str().to_string())),
1894+
_ => Err(miette!(
1895+
"Unexpected rule in arithmetic expression: {:?}",
1896+
first.as_rule()
1897+
)),
1898+
}?;
1899+
1900+
// Validate that increment/decrement are only applied to variables or expressions
1901+
if matches!(operand, ArithmeticPart::Number(_)) {
1902+
return Err(miette!(
1903+
"Increment/decrement operators cannot be applied to literal numbers"
1904+
));
18211905
}
1906+
1907+
let op = parse_post_arithmetic_op(second)?;
1908+
Ok(ArithmeticPart::UnaryArithmeticExpr {
1909+
operator: UnaryArithmeticOp::Post(op),
1910+
operand: Box::new(operand),
1911+
})
18221912
}
18231913

1824-
fn parse_unary_arithmetic_op(pair: Pair<Rule>) -> Result<UnaryArithmeticOp> {
1825-
match pair.as_str() {
1826-
"+" => Ok(UnaryArithmeticOp::Plus),
1827-
"-" => Ok(UnaryArithmeticOp::Minus),
1828-
"!" => Ok(UnaryArithmeticOp::LogicalNot),
1829-
"~" => Ok(UnaryArithmeticOp::BitwiseNot),
1914+
fn parse_pre_arithmetic_op(pair: Pair<Rule>) -> Result<PreArithmeticOp> {
1915+
let first = pair
1916+
.into_inner()
1917+
.next()
1918+
.ok_or_else(|| miette!("Expected increment or decrement operator"))?;
1919+
match first.as_rule() {
1920+
Rule::increment => Ok(PreArithmeticOp::Increment),
1921+
Rule::decrement => Ok(PreArithmeticOp::Decrement),
1922+
Rule::unary_plus => Ok(PreArithmeticOp::Plus),
1923+
Rule::unary_minus => Ok(PreArithmeticOp::Minus),
1924+
Rule::logical_not => Ok(PreArithmeticOp::LogicalNot),
1925+
Rule::bitwise_not => Ok(PreArithmeticOp::BitwiseNot),
18301926
_ => Err(miette!(
1831-
"Invalid unary arithmetic operator: {}",
1832-
pair.as_str()
1927+
"Unexpected rule in pre arithmetic operator: {:?}",
1928+
first.as_rule()
18331929
)),
18341930
}
18351931
}
18361932

18371933
fn parse_post_arithmetic_op(pair: Pair<Rule>) -> Result<PostArithmeticOp> {
1838-
match pair.as_str() {
1839-
"++" => Ok(PostArithmeticOp::Increment),
1840-
"--" => Ok(PostArithmeticOp::Decrement),
1934+
let first = pair
1935+
.into_inner()
1936+
.next()
1937+
.ok_or_else(|| miette!("Expected increment or decrement operator"))?;
1938+
match first.as_rule() {
1939+
Rule::increment => Ok(PostArithmeticOp::Increment),
1940+
Rule::decrement => Ok(PostArithmeticOp::Decrement),
18411941
_ => Err(miette!(
1842-
"Invalid post arithmetic operator: {}",
1843-
pair.as_str()
1942+
"Unexpected rule in post arithmetic operator: {:?}",
1943+
first.as_rule()
18441944
)),
18451945
}
18461946
}

crates/deno_task_shell/src/shell/execute.rs

Lines changed: 21 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,55 +15,21 @@ use thiserror::Error;
1515
use tokio::task::JoinHandle;
1616
use tokio_util::sync::CancellationToken;
1717

18-
use crate::parser::AssignmentOp;
19-
use crate::parser::BinaryOp;
20-
use crate::parser::CaseClause;
21-
use crate::parser::Condition;
22-
use crate::parser::ConditionInner;
23-
use crate::parser::ElsePart;
24-
use crate::parser::ForLoop;
25-
use crate::parser::IoFile;
26-
use crate::parser::RedirectOpInput;
27-
use crate::parser::RedirectOpOutput;
28-
use crate::parser::UnaryOp;
29-
use crate::parser::VariableModifier;
30-
use crate::parser::WhileLoop;
31-
use crate::shell::commands::ShellCommand;
32-
use crate::shell::commands::ShellCommandContext;
33-
use crate::shell::types::pipe;
34-
use crate::shell::types::ArithmeticResult;
35-
use crate::shell::types::ArithmeticValue;
36-
use crate::shell::types::EnvChange;
37-
use crate::shell::types::ExecuteResult;
38-
use crate::shell::types::FutureExecuteResult;
39-
use crate::shell::types::ShellPipeReader;
40-
use crate::shell::types::ShellPipeWriter;
41-
use crate::shell::types::ShellState;
18+
use crate::parser::{
19+
Arithmetic, ArithmeticPart, AssignmentOp, BinaryArithmeticOp, BinaryOp,
20+
CaseClause, Command, CommandInner, Condition, ConditionInner, ElsePart,
21+
ForLoop, IfClause, IoFile, PipeSequence, PipeSequenceOperator, Pipeline,
22+
PipelineInner, Redirect, RedirectFd, RedirectOp, RedirectOpInput,
23+
RedirectOpOutput, Sequence, SequentialList, SimpleCommand,
24+
UnaryArithmeticOp, UnaryOp, VariableModifier, WhileLoop, Word, WordPart,
25+
};
26+
use crate::shell::commands::{ShellCommand, ShellCommandContext};
4227
use crate::shell::types::TextPart::Text as OtherText;
43-
44-
use crate::parser::Arithmetic;
45-
use crate::parser::ArithmeticPart;
46-
use crate::parser::BinaryArithmeticOp;
47-
use crate::parser::Command;
48-
use crate::parser::CommandInner;
49-
use crate::parser::IfClause;
50-
use crate::parser::PipeSequence;
51-
use crate::parser::PipeSequenceOperator;
52-
use crate::parser::Pipeline;
53-
use crate::parser::PipelineInner;
54-
use crate::parser::Redirect;
55-
use crate::parser::RedirectFd;
56-
use crate::parser::RedirectOp;
57-
use crate::parser::Sequence;
58-
use crate::parser::SequentialList;
59-
use crate::parser::SimpleCommand;
60-
use crate::parser::UnaryArithmeticOp;
61-
use crate::parser::Word;
62-
use crate::parser::WordPart;
63-
use crate::shell::types::Text;
64-
use crate::shell::types::TextPart;
65-
use crate::shell::types::WordPartsResult;
66-
use crate::shell::types::WordResult;
28+
use crate::shell::types::{
29+
pipe, ArithmeticResult, ArithmeticValue, EnvChange, ExecuteResult,
30+
FutureExecuteResult, ShellPipeReader, ShellPipeWriter, ShellState, Text,
31+
TextPart, WordPartsResult, WordResult,
32+
};
6733

6834
use super::command::execute_unresolved_command_name;
6935
use super::command::UnresolvedCommandName;
@@ -1071,12 +1037,7 @@ async fn evaluate_arithmetic_part(
10711037
ArithmeticPart::UnaryArithmeticExpr { operator, operand } => {
10721038
let val =
10731039
Box::pin(evaluate_arithmetic_part(operand, state)).await?;
1074-
apply_unary_op(*operator, val)
1075-
}
1076-
ArithmeticPart::PostArithmeticExpr { operand, .. } => {
1077-
let val =
1078-
Box::pin(evaluate_arithmetic_part(operand, state)).await?;
1079-
Ok(val)
1040+
apply_unary_op(state, *operator, val, operand)
10801041
}
10811042
ArithmeticPart::Variable(name) => state
10821043
.get_var(name)
@@ -1164,19 +1125,15 @@ fn apply_conditional_binary_op(
11641125
}
11651126

11661127
fn apply_unary_op(
1128+
state: &mut ShellState,
11671129
op: UnaryArithmeticOp,
11681130
val: ArithmeticResult,
1131+
operand: &ArithmeticPart,
11691132
) -> Result<ArithmeticResult, Error> {
1170-
match op {
1171-
UnaryArithmeticOp::Plus => Ok(val),
1172-
UnaryArithmeticOp::Minus => val.checked_neg(),
1173-
UnaryArithmeticOp::LogicalNot => Ok(if val.is_zero() {
1174-
ArithmeticResult::new(ArithmeticValue::Integer(1))
1175-
} else {
1176-
ArithmeticResult::new(ArithmeticValue::Integer(0))
1177-
}),
1178-
UnaryArithmeticOp::BitwiseNot => val.checked_not(),
1179-
}
1133+
let result = val.unary_op(operand, op)?;
1134+
let result_clone = result.clone();
1135+
state.apply_changes(&result_clone.changes);
1136+
Ok(result)
11801137
}
11811138

11821139
async fn execute_pipe_sequence(

0 commit comments

Comments
 (0)