summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Smrž <roman.smrz@seznam.cz>2026-09-04 19:25:09 +0200
committerRoman Smrž <roman.smrz@seznam.cz>2026-09-05 21:41:39 +0200
commita9439b66ad8f7a8f4ba079a47ecfe1e76c2abc4b (patch)
tree7ef225d63462349b8f5708cdceecc9010734e60a
parentc2c89cf5308ea5c490a4fe150cffc72ed09c4c1c (diff)
Expression expansion for shell arguments
Changelog: Lists can now be dollar-expanded in the shell interpreter to provide list of shell command arguments.
-rw-r--r--src/Parser/Expr.hs1
-rw-r--r--src/Parser/Shell.hs8
-rw-r--r--src/Script/Shell.hs18
-rw-r--r--test/asset/shell/expansion.et13
-rw-r--r--test/script/shell.et17
5 files changed, 56 insertions, 1 deletions
diff --git a/src/Parser/Expr.hs b/src/Parser/Expr.hs
index 89fa208..2d0b770 100644
--- a/src/Parser/Expr.hs
+++ b/src/Parser/Expr.hs
@@ -14,6 +14,7 @@ module Parser.Expr (
variable,
constructor,
+ expressionExpansion,
stringExpansion,
functionArguments,
diff --git a/src/Parser/Shell.hs b/src/Parser/Shell.hs
index 45f00b9..a09eb43 100644
--- a/src/Parser/Shell.hs
+++ b/src/Parser/Shell.hs
@@ -92,11 +92,17 @@ parseRedirection = choice
parseArgument :: TestParser (Expr ShellArgument)
parseArgument = choice
[ parseRedirection
+ , expressionExpansion "shell argument" <* sc
, fmap ShellArgument <$> parseTextArgument
]
parseArguments :: TestParser (Expr ShellArguments)
-parseArguments = (ShellArguments <$>) . foldr (liftA2 (:)) (Pure []) <$> many parseArgument
+parseArguments = do
+ arglists <- many $ choice
+ [ expressionExpansion "shell arguments" <* sc
+ , fmap (ShellArguments . (: [])) <$> parseArgument
+ ]
+ return $ fmap mconcat $ foldr (liftA2 (:)) (Pure []) $ arglists
parseCommand :: TestParser (Expr ShellCommand)
parseCommand = label "shell statement" $ do
diff --git a/src/Script/Shell.hs b/src/Script/Shell.hs
index 3e98e66..e7c8542 100644
--- a/src/Script/Shell.hs
+++ b/src/Script/Shell.hs
@@ -15,8 +15,10 @@ import Control.Monad.IO.Class
import Control.Monad.Reader
import Data.Maybe
+import Data.Scientific
import Data.Text (Text)
import Data.Text qualified as T
+import Data.Typeable
import Foreign.C.Types
import Foreign.Ptr
@@ -67,6 +69,7 @@ data ShellCommand = ShellCommand
}
newtype ShellArguments = ShellArguments { fromShellArguments :: [ ShellArgument ] }
+ deriving (Semigroup, Monoid)
data ShellArgument
= ShellArgument Text
@@ -97,10 +100,25 @@ instance ExprType ShellArguments where
textExprType _ = T.pack "ShellArguments"
textExprValue _ = "<shell-arguments>"
+ exprExpansionConvFrom = listToMaybe $ catMaybes
+ [ cast (ShellArguments . (: []) . ShellArgument)
+ , cast (ShellArguments . (: []) . ShellArgument . T.pack . show @Integer)
+ , cast (ShellArguments . (: []) . ShellArgument . T.pack . show @Scientific)
+ , cast (ShellArguments . map ShellArgument)
+ , cast (ShellArguments . map (ShellArgument . T.pack . show @Integer))
+ , cast (ShellArguments . map (ShellArgument . T.pack . show @Scientific))
+ ]
+
instance ExprType ShellArgument where
textExprType _ = T.pack "ShellArgument"
textExprValue _ = "<shell-argument>"
+ exprExpansionConvFrom = listToMaybe $ catMaybes
+ [ cast (ShellArgument)
+ , cast (ShellArgument . T.pack . show @Integer)
+ , cast (ShellArgument . T.pack . show @Scientific)
+ ]
+
data ShellExecInfo = ShellExecInfo
{ seiNode :: Node
diff --git a/test/asset/shell/expansion.et b/test/asset/shell/expansion.et
new file mode 100644
index 0000000..050efba
--- /dev/null
+++ b/test/asset/shell/expansion.et
@@ -0,0 +1,13 @@
+test Test:
+ node n
+
+ let str = "some string"
+ let int = 2
+ let num = 4.5
+ let list = [ "a b", "c d", "e", "f" ]
+ let ilist = [ 1 .. 5 ]
+
+ shell on n as sh:
+ echo A $str B $int $num C
+ echo D $list E
+ echo F $ilist G
diff --git a/test/script/shell.et b/test/script/shell.et
index 3384353..4dca4a2 100644
--- a/test/script/shell.et
+++ b/test/script/shell.et
@@ -180,3 +180,20 @@ test ShellLogic:
expect /run-test-result NegateNothing failed/
expect /run-done 1 0 0 1/
flush
+
+
+test ShellVariableExpansion:
+ spawn as p
+ with p:
+ send "load ${scripts.path}/expansion.et"
+ local:
+ expect /(load-.*)/ capture done
+ guard (done == "load-done")
+
+ send "run Test"
+ expect /child-stdout sh A some string B 2 4.5 C/
+ expect /child-stdout sh D a b c d e f E/
+ expect /child-stdout sh F 1 2 3 4 5 G/
+ expect /run-test-result Test done/
+ expect /run-done 1 1 0 0/
+ flush