just-bash 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. just_bash/__init__.py +55 -0
  2. just_bash/ast/__init__.py +213 -0
  3. just_bash/ast/factory.py +320 -0
  4. just_bash/ast/types.py +953 -0
  5. just_bash/bash.py +220 -0
  6. just_bash/commands/__init__.py +23 -0
  7. just_bash/commands/argv/__init__.py +5 -0
  8. just_bash/commands/argv/argv.py +21 -0
  9. just_bash/commands/awk/__init__.py +5 -0
  10. just_bash/commands/awk/awk.py +1168 -0
  11. just_bash/commands/base64/__init__.py +5 -0
  12. just_bash/commands/base64/base64.py +138 -0
  13. just_bash/commands/basename/__init__.py +5 -0
  14. just_bash/commands/basename/basename.py +72 -0
  15. just_bash/commands/bash/__init__.py +5 -0
  16. just_bash/commands/bash/bash.py +188 -0
  17. just_bash/commands/cat/__init__.py +5 -0
  18. just_bash/commands/cat/cat.py +173 -0
  19. just_bash/commands/checksum/__init__.py +5 -0
  20. just_bash/commands/checksum/checksum.py +179 -0
  21. just_bash/commands/chmod/__init__.py +5 -0
  22. just_bash/commands/chmod/chmod.py +216 -0
  23. just_bash/commands/column/__init__.py +5 -0
  24. just_bash/commands/column/column.py +180 -0
  25. just_bash/commands/comm/__init__.py +5 -0
  26. just_bash/commands/comm/comm.py +150 -0
  27. just_bash/commands/compression/__init__.py +5 -0
  28. just_bash/commands/compression/compression.py +298 -0
  29. just_bash/commands/cp/__init__.py +5 -0
  30. just_bash/commands/cp/cp.py +149 -0
  31. just_bash/commands/curl/__init__.py +5 -0
  32. just_bash/commands/curl/curl.py +801 -0
  33. just_bash/commands/cut/__init__.py +5 -0
  34. just_bash/commands/cut/cut.py +327 -0
  35. just_bash/commands/date/__init__.py +5 -0
  36. just_bash/commands/date/date.py +258 -0
  37. just_bash/commands/diff/__init__.py +5 -0
  38. just_bash/commands/diff/diff.py +118 -0
  39. just_bash/commands/dirname/__init__.py +5 -0
  40. just_bash/commands/dirname/dirname.py +56 -0
  41. just_bash/commands/du/__init__.py +5 -0
  42. just_bash/commands/du/du.py +150 -0
  43. just_bash/commands/echo/__init__.py +5 -0
  44. just_bash/commands/echo/echo.py +125 -0
  45. just_bash/commands/env/__init__.py +5 -0
  46. just_bash/commands/env/env.py +163 -0
  47. just_bash/commands/expand/__init__.py +5 -0
  48. just_bash/commands/expand/expand.py +299 -0
  49. just_bash/commands/expr/__init__.py +5 -0
  50. just_bash/commands/expr/expr.py +273 -0
  51. just_bash/commands/file/__init__.py +5 -0
  52. just_bash/commands/file/file.py +274 -0
  53. just_bash/commands/find/__init__.py +5 -0
  54. just_bash/commands/find/find.py +623 -0
  55. just_bash/commands/fold/__init__.py +5 -0
  56. just_bash/commands/fold/fold.py +160 -0
  57. just_bash/commands/grep/__init__.py +5 -0
  58. just_bash/commands/grep/grep.py +418 -0
  59. just_bash/commands/head/__init__.py +5 -0
  60. just_bash/commands/head/head.py +167 -0
  61. just_bash/commands/help/__init__.py +5 -0
  62. just_bash/commands/help/help.py +67 -0
  63. just_bash/commands/hostname/__init__.py +5 -0
  64. just_bash/commands/hostname/hostname.py +21 -0
  65. just_bash/commands/html_to_markdown/__init__.py +5 -0
  66. just_bash/commands/html_to_markdown/html_to_markdown.py +191 -0
  67. just_bash/commands/join/__init__.py +5 -0
  68. just_bash/commands/join/join.py +252 -0
  69. just_bash/commands/jq/__init__.py +5 -0
  70. just_bash/commands/jq/jq.py +280 -0
  71. just_bash/commands/ln/__init__.py +5 -0
  72. just_bash/commands/ln/ln.py +127 -0
  73. just_bash/commands/ls/__init__.py +5 -0
  74. just_bash/commands/ls/ls.py +280 -0
  75. just_bash/commands/mkdir/__init__.py +5 -0
  76. just_bash/commands/mkdir/mkdir.py +92 -0
  77. just_bash/commands/mv/__init__.py +5 -0
  78. just_bash/commands/mv/mv.py +142 -0
  79. just_bash/commands/nl/__init__.py +5 -0
  80. just_bash/commands/nl/nl.py +180 -0
  81. just_bash/commands/od/__init__.py +5 -0
  82. just_bash/commands/od/od.py +157 -0
  83. just_bash/commands/paste/__init__.py +5 -0
  84. just_bash/commands/paste/paste.py +100 -0
  85. just_bash/commands/printf/__init__.py +5 -0
  86. just_bash/commands/printf/printf.py +157 -0
  87. just_bash/commands/pwd/__init__.py +5 -0
  88. just_bash/commands/pwd/pwd.py +23 -0
  89. just_bash/commands/read/__init__.py +5 -0
  90. just_bash/commands/read/read.py +185 -0
  91. just_bash/commands/readlink/__init__.py +5 -0
  92. just_bash/commands/readlink/readlink.py +86 -0
  93. just_bash/commands/registry.py +844 -0
  94. just_bash/commands/rev/__init__.py +5 -0
  95. just_bash/commands/rev/rev.py +74 -0
  96. just_bash/commands/rg/__init__.py +5 -0
  97. just_bash/commands/rg/rg.py +1048 -0
  98. just_bash/commands/rm/__init__.py +5 -0
  99. just_bash/commands/rm/rm.py +106 -0
  100. just_bash/commands/search_engine/__init__.py +13 -0
  101. just_bash/commands/search_engine/matcher.py +170 -0
  102. just_bash/commands/search_engine/regex.py +159 -0
  103. just_bash/commands/sed/__init__.py +5 -0
  104. just_bash/commands/sed/sed.py +863 -0
  105. just_bash/commands/seq/__init__.py +5 -0
  106. just_bash/commands/seq/seq.py +190 -0
  107. just_bash/commands/shell/__init__.py +5 -0
  108. just_bash/commands/shell/shell.py +206 -0
  109. just_bash/commands/sleep/__init__.py +5 -0
  110. just_bash/commands/sleep/sleep.py +62 -0
  111. just_bash/commands/sort/__init__.py +5 -0
  112. just_bash/commands/sort/sort.py +411 -0
  113. just_bash/commands/split/__init__.py +5 -0
  114. just_bash/commands/split/split.py +237 -0
  115. just_bash/commands/sqlite3/__init__.py +5 -0
  116. just_bash/commands/sqlite3/sqlite3_cmd.py +505 -0
  117. just_bash/commands/stat/__init__.py +5 -0
  118. just_bash/commands/stat/stat.py +150 -0
  119. just_bash/commands/strings/__init__.py +5 -0
  120. just_bash/commands/strings/strings.py +150 -0
  121. just_bash/commands/tac/__init__.py +5 -0
  122. just_bash/commands/tac/tac.py +158 -0
  123. just_bash/commands/tail/__init__.py +5 -0
  124. just_bash/commands/tail/tail.py +180 -0
  125. just_bash/commands/tar/__init__.py +5 -0
  126. just_bash/commands/tar/tar.py +1067 -0
  127. just_bash/commands/tee/__init__.py +5 -0
  128. just_bash/commands/tee/tee.py +63 -0
  129. just_bash/commands/timeout/__init__.py +5 -0
  130. just_bash/commands/timeout/timeout.py +188 -0
  131. just_bash/commands/touch/__init__.py +5 -0
  132. just_bash/commands/touch/touch.py +91 -0
  133. just_bash/commands/tr/__init__.py +5 -0
  134. just_bash/commands/tr/tr.py +297 -0
  135. just_bash/commands/tree/__init__.py +5 -0
  136. just_bash/commands/tree/tree.py +139 -0
  137. just_bash/commands/true/__init__.py +5 -0
  138. just_bash/commands/true/true.py +32 -0
  139. just_bash/commands/uniq/__init__.py +5 -0
  140. just_bash/commands/uniq/uniq.py +323 -0
  141. just_bash/commands/wc/__init__.py +5 -0
  142. just_bash/commands/wc/wc.py +169 -0
  143. just_bash/commands/which/__init__.py +5 -0
  144. just_bash/commands/which/which.py +52 -0
  145. just_bash/commands/xan/__init__.py +5 -0
  146. just_bash/commands/xan/xan.py +1663 -0
  147. just_bash/commands/xargs/__init__.py +5 -0
  148. just_bash/commands/xargs/xargs.py +136 -0
  149. just_bash/commands/yq/__init__.py +5 -0
  150. just_bash/commands/yq/yq.py +848 -0
  151. just_bash/fs/__init__.py +29 -0
  152. just_bash/fs/in_memory_fs.py +621 -0
  153. just_bash/fs/mountable_fs.py +504 -0
  154. just_bash/fs/overlay_fs.py +894 -0
  155. just_bash/fs/read_write_fs.py +455 -0
  156. just_bash/interpreter/__init__.py +37 -0
  157. just_bash/interpreter/builtins/__init__.py +92 -0
  158. just_bash/interpreter/builtins/alias.py +154 -0
  159. just_bash/interpreter/builtins/cd.py +76 -0
  160. just_bash/interpreter/builtins/control.py +127 -0
  161. just_bash/interpreter/builtins/declare.py +336 -0
  162. just_bash/interpreter/builtins/export.py +56 -0
  163. just_bash/interpreter/builtins/let.py +44 -0
  164. just_bash/interpreter/builtins/local.py +57 -0
  165. just_bash/interpreter/builtins/mapfile.py +152 -0
  166. just_bash/interpreter/builtins/misc.py +378 -0
  167. just_bash/interpreter/builtins/readonly.py +80 -0
  168. just_bash/interpreter/builtins/set.py +234 -0
  169. just_bash/interpreter/builtins/shopt.py +201 -0
  170. just_bash/interpreter/builtins/source.py +136 -0
  171. just_bash/interpreter/builtins/test.py +290 -0
  172. just_bash/interpreter/builtins/unset.py +53 -0
  173. just_bash/interpreter/conditionals.py +387 -0
  174. just_bash/interpreter/control_flow.py +381 -0
  175. just_bash/interpreter/errors.py +116 -0
  176. just_bash/interpreter/expansion.py +1156 -0
  177. just_bash/interpreter/interpreter.py +813 -0
  178. just_bash/interpreter/types.py +134 -0
  179. just_bash/network/__init__.py +1 -0
  180. just_bash/parser/__init__.py +39 -0
  181. just_bash/parser/lexer.py +948 -0
  182. just_bash/parser/parser.py +2162 -0
  183. just_bash/py.typed +0 -0
  184. just_bash/query_engine/__init__.py +83 -0
  185. just_bash/query_engine/builtins/__init__.py +1283 -0
  186. just_bash/query_engine/evaluator.py +578 -0
  187. just_bash/query_engine/parser.py +525 -0
  188. just_bash/query_engine/tokenizer.py +329 -0
  189. just_bash/query_engine/types.py +373 -0
  190. just_bash/types.py +180 -0
  191. just_bash-0.1.5.dist-info/METADATA +410 -0
  192. just_bash-0.1.5.dist-info/RECORD +193 -0
  193. just_bash-0.1.5.dist-info/WHEEL +4 -0
just_bash/__init__.py ADDED
@@ -0,0 +1,55 @@
1
+ """
2
+ just-bash: A pure Python bash interpreter with in-memory virtual filesystem.
3
+
4
+ Designed for AI agents needing a secure, sandboxed bash environment.
5
+
6
+ Example usage:
7
+ from just_bash import Bash
8
+
9
+ bash = Bash()
10
+ result = await bash.exec("echo hello world")
11
+ print(result.stdout) # "hello world\\n"
12
+ """
13
+
14
+ from .bash import Bash
15
+ from .types import (
16
+ ExecResult,
17
+ BashExecResult,
18
+ ExecutionLimits,
19
+ NetworkConfig,
20
+ IFileSystem,
21
+ FsStat,
22
+ CommandContext,
23
+ Command,
24
+ OK,
25
+ FAIL,
26
+ )
27
+ from .fs import InMemoryFs
28
+ from .parser import Parser, parse, ParseException
29
+
30
+ __version__ = "0.1.0"
31
+
32
+ __all__ = [
33
+ # Main API
34
+ "Bash",
35
+ # Core types
36
+ "ExecResult",
37
+ "BashExecResult",
38
+ "ExecutionLimits",
39
+ "NetworkConfig",
40
+ "IFileSystem",
41
+ "FsStat",
42
+ "CommandContext",
43
+ "Command",
44
+ # Filesystem
45
+ "InMemoryFs",
46
+ # Parser
47
+ "Parser",
48
+ "parse",
49
+ "ParseException",
50
+ # Constants
51
+ "OK",
52
+ "FAIL",
53
+ # Version
54
+ "__version__",
55
+ ]
@@ -0,0 +1,213 @@
1
+ """AST types and factory functions for just-bash."""
2
+
3
+ from .types import (
4
+ # Base types
5
+ Position,
6
+ Span,
7
+ # Script & Statements
8
+ ScriptNode,
9
+ StatementNode,
10
+ # Pipelines & Commands
11
+ PipelineNode,
12
+ SimpleCommandNode,
13
+ CommandNode,
14
+ CompoundCommandNode,
15
+ # Control flow
16
+ IfNode,
17
+ IfClause,
18
+ ForNode,
19
+ CStyleForNode,
20
+ WhileNode,
21
+ UntilNode,
22
+ CaseNode,
23
+ CaseItemNode,
24
+ SubshellNode,
25
+ GroupNode,
26
+ ArithmeticCommandNode,
27
+ ConditionalCommandNode,
28
+ # Functions
29
+ FunctionDefNode,
30
+ # Assignments
31
+ AssignmentNode,
32
+ # Redirections
33
+ RedirectionNode,
34
+ RedirectionOperator,
35
+ HereDocNode,
36
+ # Words
37
+ WordNode,
38
+ WordPart,
39
+ LiteralPart,
40
+ SingleQuotedPart,
41
+ DoubleQuotedPart,
42
+ EscapedPart,
43
+ GlobPart,
44
+ TildeExpansionPart,
45
+ # Parameter expansion
46
+ ParameterExpansionPart,
47
+ ParameterOperation,
48
+ DefaultValueOp,
49
+ AssignDefaultOp,
50
+ ErrorIfUnsetOp,
51
+ UseAlternativeOp,
52
+ LengthOp,
53
+ LengthSliceErrorOp,
54
+ SubstringOp,
55
+ PatternRemovalOp,
56
+ PatternReplacementOp,
57
+ CaseModificationOp,
58
+ TransformOp,
59
+ IndirectionOp,
60
+ ArrayKeysOp,
61
+ VarNamePrefixOp,
62
+ # Command substitution
63
+ CommandSubstitutionPart,
64
+ # Arithmetic
65
+ ArithmeticExpansionPart,
66
+ ArithmeticExpressionNode,
67
+ ArithExpr,
68
+ ArithNumberNode,
69
+ ArithVariableNode,
70
+ ArithBinaryNode,
71
+ ArithBinaryOperator,
72
+ ArithUnaryNode,
73
+ ArithUnaryOperator,
74
+ ArithTernaryNode,
75
+ ArithAssignmentNode,
76
+ ArithAssignmentOperator,
77
+ ArithGroupNode,
78
+ ArithNestedNode,
79
+ ArithCommandSubstNode,
80
+ ArithBracedExpansionNode,
81
+ ArithDynamicBaseNode,
82
+ ArithDynamicNumberNode,
83
+ ArithConcatNode,
84
+ ArithArrayElementNode,
85
+ ArithDoubleSubscriptNode,
86
+ ArithNumberSubscriptNode,
87
+ # Process substitution
88
+ ProcessSubstitutionPart,
89
+ # Brace expansion
90
+ BraceExpansionPart,
91
+ BraceItem,
92
+ BraceWordItem,
93
+ BraceRangeItem,
94
+ # Conditional expressions
95
+ ConditionalExpressionNode,
96
+ CondBinaryNode,
97
+ CondBinaryOperator,
98
+ CondUnaryNode,
99
+ CondUnaryOperator,
100
+ CondNotNode,
101
+ CondAndNode,
102
+ CondOrNode,
103
+ CondGroupNode,
104
+ CondWordNode,
105
+ )
106
+
107
+ from . import factory as AST
108
+
109
+ __all__ = [
110
+ # Base types
111
+ "Position",
112
+ "Span",
113
+ # Script & Statements
114
+ "ScriptNode",
115
+ "StatementNode",
116
+ # Pipelines & Commands
117
+ "PipelineNode",
118
+ "SimpleCommandNode",
119
+ "CommandNode",
120
+ "CompoundCommandNode",
121
+ # Control flow
122
+ "IfNode",
123
+ "IfClause",
124
+ "ForNode",
125
+ "CStyleForNode",
126
+ "WhileNode",
127
+ "UntilNode",
128
+ "CaseNode",
129
+ "CaseItemNode",
130
+ "SubshellNode",
131
+ "GroupNode",
132
+ "ArithmeticCommandNode",
133
+ "ConditionalCommandNode",
134
+ # Functions
135
+ "FunctionDefNode",
136
+ # Assignments
137
+ "AssignmentNode",
138
+ # Redirections
139
+ "RedirectionNode",
140
+ "RedirectionOperator",
141
+ "HereDocNode",
142
+ # Words
143
+ "WordNode",
144
+ "WordPart",
145
+ "LiteralPart",
146
+ "SingleQuotedPart",
147
+ "DoubleQuotedPart",
148
+ "EscapedPart",
149
+ "GlobPart",
150
+ "TildeExpansionPart",
151
+ # Parameter expansion
152
+ "ParameterExpansionPart",
153
+ "ParameterOperation",
154
+ "DefaultValueOp",
155
+ "AssignDefaultOp",
156
+ "ErrorIfUnsetOp",
157
+ "UseAlternativeOp",
158
+ "LengthOp",
159
+ "LengthSliceErrorOp",
160
+ "SubstringOp",
161
+ "PatternRemovalOp",
162
+ "PatternReplacementOp",
163
+ "CaseModificationOp",
164
+ "TransformOp",
165
+ "IndirectionOp",
166
+ "ArrayKeysOp",
167
+ "VarNamePrefixOp",
168
+ # Command substitution
169
+ "CommandSubstitutionPart",
170
+ # Arithmetic
171
+ "ArithmeticExpansionPart",
172
+ "ArithmeticExpressionNode",
173
+ "ArithExpr",
174
+ "ArithNumberNode",
175
+ "ArithVariableNode",
176
+ "ArithBinaryNode",
177
+ "ArithBinaryOperator",
178
+ "ArithUnaryNode",
179
+ "ArithUnaryOperator",
180
+ "ArithTernaryNode",
181
+ "ArithAssignmentNode",
182
+ "ArithAssignmentOperator",
183
+ "ArithGroupNode",
184
+ "ArithNestedNode",
185
+ "ArithCommandSubstNode",
186
+ "ArithBracedExpansionNode",
187
+ "ArithDynamicBaseNode",
188
+ "ArithDynamicNumberNode",
189
+ "ArithConcatNode",
190
+ "ArithArrayElementNode",
191
+ "ArithDoubleSubscriptNode",
192
+ "ArithNumberSubscriptNode",
193
+ # Process substitution
194
+ "ProcessSubstitutionPart",
195
+ # Brace expansion
196
+ "BraceExpansionPart",
197
+ "BraceItem",
198
+ "BraceWordItem",
199
+ "BraceRangeItem",
200
+ # Conditional expressions
201
+ "ConditionalExpressionNode",
202
+ "CondBinaryNode",
203
+ "CondBinaryOperator",
204
+ "CondUnaryNode",
205
+ "CondUnaryOperator",
206
+ "CondNotNode",
207
+ "CondAndNode",
208
+ "CondOrNode",
209
+ "CondGroupNode",
210
+ "CondWordNode",
211
+ # Factory module
212
+ "AST",
213
+ ]
@@ -0,0 +1,320 @@
1
+ """
2
+ AST Factory Functions
3
+
4
+ Convenience functions for building AST nodes, matching the TypeScript AST object pattern.
5
+ """
6
+
7
+ from typing import Optional, Sequence
8
+
9
+ from .types import (
10
+ # Core nodes
11
+ ScriptNode,
12
+ StatementNode,
13
+ PipelineNode,
14
+ SimpleCommandNode,
15
+ WordNode,
16
+ WordPart,
17
+ AssignmentNode,
18
+ RedirectionNode,
19
+ RedirectionOperator,
20
+ HereDocNode,
21
+ # Word parts
22
+ LiteralPart,
23
+ SingleQuotedPart,
24
+ DoubleQuotedPart,
25
+ EscapedPart,
26
+ ParameterExpansionPart,
27
+ ParameterOperation,
28
+ CommandSubstitutionPart,
29
+ ArithmeticExpansionPart,
30
+ ArithmeticExpressionNode,
31
+ # Control flow
32
+ IfNode,
33
+ IfClause,
34
+ ForNode,
35
+ WhileNode,
36
+ UntilNode,
37
+ CaseNode,
38
+ CaseItemNode,
39
+ SubshellNode,
40
+ GroupNode,
41
+ FunctionDefNode,
42
+ ConditionalCommandNode,
43
+ ArithmeticCommandNode,
44
+ CompoundCommandNode,
45
+ ConditionalExpressionNode,
46
+ )
47
+
48
+
49
+ def script(statements: Sequence[StatementNode]) -> ScriptNode:
50
+ """Create a script node."""
51
+ return ScriptNode(statements=tuple(statements))
52
+
53
+
54
+ def statement(
55
+ pipelines: Sequence[PipelineNode],
56
+ operators: Sequence[str] | None = None,
57
+ background: bool = False,
58
+ ) -> StatementNode:
59
+ """Create a statement node."""
60
+ ops = tuple(operators) if operators else ()
61
+ return StatementNode(
62
+ pipelines=tuple(pipelines),
63
+ operators=ops, # type: ignore
64
+ background=background,
65
+ )
66
+
67
+
68
+ def pipeline(
69
+ commands: Sequence["CommandNode"], # type: ignore
70
+ negated: bool = False,
71
+ ) -> PipelineNode:
72
+ """Create a pipeline node."""
73
+ return PipelineNode(commands=tuple(commands), negated=negated)
74
+
75
+
76
+ def simple_command(
77
+ name: WordNode | None = None,
78
+ args: Sequence[WordNode] | None = None,
79
+ assignments: Sequence[AssignmentNode] | None = None,
80
+ redirections: Sequence[RedirectionNode] | None = None,
81
+ ) -> SimpleCommandNode:
82
+ """Create a simple command node."""
83
+ return SimpleCommandNode(
84
+ name=name,
85
+ args=tuple(args) if args else (),
86
+ assignments=tuple(assignments) if assignments else (),
87
+ redirections=tuple(redirections) if redirections else (),
88
+ )
89
+
90
+
91
+ def word(parts: Sequence[WordPart]) -> WordNode:
92
+ """Create a word node."""
93
+ return WordNode(parts=tuple(parts))
94
+
95
+
96
+ def literal(value: str) -> LiteralPart:
97
+ """Create a literal part."""
98
+ return LiteralPart(value=value)
99
+
100
+
101
+ def single_quoted(value: str) -> SingleQuotedPart:
102
+ """Create a single-quoted part."""
103
+ return SingleQuotedPart(value=value)
104
+
105
+
106
+ def double_quoted(parts: Sequence[WordPart]) -> DoubleQuotedPart:
107
+ """Create a double-quoted part."""
108
+ return DoubleQuotedPart(parts=tuple(parts))
109
+
110
+
111
+ def escaped(value: str) -> EscapedPart:
112
+ """Create an escaped character part."""
113
+ return EscapedPart(value=value)
114
+
115
+
116
+ def parameter_expansion(
117
+ parameter: str,
118
+ operation: ParameterOperation | None = None,
119
+ ) -> ParameterExpansionPart:
120
+ """Create a parameter expansion part."""
121
+ return ParameterExpansionPart(parameter=parameter, operation=operation)
122
+
123
+
124
+ def command_substitution(
125
+ body: ScriptNode,
126
+ legacy: bool = False,
127
+ ) -> CommandSubstitutionPart:
128
+ """Create a command substitution part."""
129
+ return CommandSubstitutionPart(body=body, legacy=legacy)
130
+
131
+
132
+ def arithmetic_expansion(
133
+ expression: ArithmeticExpressionNode,
134
+ ) -> ArithmeticExpansionPart:
135
+ """Create an arithmetic expansion part."""
136
+ return ArithmeticExpansionPart(expression=expression)
137
+
138
+
139
+ def assignment(
140
+ name: str,
141
+ value: WordNode | None = None,
142
+ append: bool = False,
143
+ array: Sequence[WordNode] | None = None,
144
+ ) -> AssignmentNode:
145
+ """Create an assignment node."""
146
+ return AssignmentNode(
147
+ name=name,
148
+ value=value,
149
+ append=append,
150
+ array=tuple(array) if array else None,
151
+ )
152
+
153
+
154
+ def redirection(
155
+ operator: RedirectionOperator,
156
+ target: WordNode | HereDocNode,
157
+ fd: int | None = None,
158
+ ) -> RedirectionNode:
159
+ """Create a redirection node."""
160
+ return RedirectionNode(fd=fd, operator=operator, target=target)
161
+
162
+
163
+ def here_doc(
164
+ delimiter: str,
165
+ content: WordNode,
166
+ strip_tabs: bool = False,
167
+ quoted: bool = False,
168
+ ) -> HereDocNode:
169
+ """Create a here-document node."""
170
+ return HereDocNode(
171
+ delimiter=delimiter,
172
+ content=content,
173
+ strip_tabs=strip_tabs,
174
+ quoted=quoted,
175
+ )
176
+
177
+
178
+ def if_node(
179
+ clauses: Sequence[IfClause],
180
+ else_body: Sequence[StatementNode] | None = None,
181
+ redirections: Sequence[RedirectionNode] | None = None,
182
+ ) -> IfNode:
183
+ """Create an if node."""
184
+ return IfNode(
185
+ clauses=tuple(clauses),
186
+ else_body=tuple(else_body) if else_body else None,
187
+ redirections=tuple(redirections) if redirections else (),
188
+ )
189
+
190
+
191
+ def if_clause(
192
+ condition: Sequence[StatementNode],
193
+ body: Sequence[StatementNode],
194
+ ) -> IfClause:
195
+ """Create an if clause."""
196
+ return IfClause(condition=tuple(condition), body=tuple(body))
197
+
198
+
199
+ def for_node(
200
+ variable: str,
201
+ words: Sequence[WordNode] | None,
202
+ body: Sequence[StatementNode],
203
+ redirections: Sequence[RedirectionNode] | None = None,
204
+ ) -> ForNode:
205
+ """Create a for node."""
206
+ return ForNode(
207
+ variable=variable,
208
+ words=tuple(words) if words else None,
209
+ body=tuple(body),
210
+ redirections=tuple(redirections) if redirections else (),
211
+ )
212
+
213
+
214
+ def while_node(
215
+ condition: Sequence[StatementNode],
216
+ body: Sequence[StatementNode],
217
+ redirections: Sequence[RedirectionNode] | None = None,
218
+ ) -> WhileNode:
219
+ """Create a while node."""
220
+ return WhileNode(
221
+ condition=tuple(condition),
222
+ body=tuple(body),
223
+ redirections=tuple(redirections) if redirections else (),
224
+ )
225
+
226
+
227
+ def until_node(
228
+ condition: Sequence[StatementNode],
229
+ body: Sequence[StatementNode],
230
+ redirections: Sequence[RedirectionNode] | None = None,
231
+ ) -> UntilNode:
232
+ """Create an until node."""
233
+ return UntilNode(
234
+ condition=tuple(condition),
235
+ body=tuple(body),
236
+ redirections=tuple(redirections) if redirections else (),
237
+ )
238
+
239
+
240
+ def case_node(
241
+ word: WordNode,
242
+ items: Sequence[CaseItemNode],
243
+ redirections: Sequence[RedirectionNode] | None = None,
244
+ ) -> CaseNode:
245
+ """Create a case node."""
246
+ return CaseNode(
247
+ word=word,
248
+ items=tuple(items),
249
+ redirections=tuple(redirections) if redirections else (),
250
+ )
251
+
252
+
253
+ def case_item(
254
+ patterns: Sequence[WordNode],
255
+ body: Sequence[StatementNode],
256
+ terminator: str = ";;",
257
+ ) -> CaseItemNode:
258
+ """Create a case item node."""
259
+ return CaseItemNode(
260
+ patterns=tuple(patterns),
261
+ body=tuple(body),
262
+ terminator=terminator, # type: ignore
263
+ )
264
+
265
+
266
+ def subshell(
267
+ body: Sequence[StatementNode],
268
+ redirections: Sequence[RedirectionNode] | None = None,
269
+ ) -> SubshellNode:
270
+ """Create a subshell node."""
271
+ return SubshellNode(
272
+ body=tuple(body),
273
+ redirections=tuple(redirections) if redirections else (),
274
+ )
275
+
276
+
277
+ def group(
278
+ body: Sequence[StatementNode],
279
+ redirections: Sequence[RedirectionNode] | None = None,
280
+ ) -> GroupNode:
281
+ """Create a command group node."""
282
+ return GroupNode(
283
+ body=tuple(body),
284
+ redirections=tuple(redirections) if redirections else (),
285
+ )
286
+
287
+
288
+ def function_def(
289
+ name: str,
290
+ body: CompoundCommandNode,
291
+ redirections: Sequence[RedirectionNode] | None = None,
292
+ ) -> FunctionDefNode:
293
+ """Create a function definition node."""
294
+ return FunctionDefNode(
295
+ name=name,
296
+ body=body,
297
+ redirections=tuple(redirections) if redirections else (),
298
+ )
299
+
300
+
301
+ def conditional_command(
302
+ expression: ConditionalExpressionNode,
303
+ redirections: Sequence[RedirectionNode] | None = None,
304
+ ) -> ConditionalCommandNode:
305
+ """Create a conditional command node ([[ ]])."""
306
+ return ConditionalCommandNode(
307
+ expression=expression,
308
+ redirections=tuple(redirections) if redirections else (),
309
+ )
310
+
311
+
312
+ def arithmetic_command(
313
+ expression: ArithmeticExpressionNode,
314
+ redirections: Sequence[RedirectionNode] | None = None,
315
+ ) -> ArithmeticCommandNode:
316
+ """Create an arithmetic command node ((( )))."""
317
+ return ArithmeticCommandNode(
318
+ expression=expression,
319
+ redirections=tuple(redirections) if redirections else (),
320
+ )