Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge changes in to support parsing bash scripts #737

Open
wants to merge 41 commits into
base: future
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5931c60
starting to integrate bash into pash
sethsabar Mar 19, 2024
132d180
[untested] add bash expansion for bash parser
sethsabar Mar 21, 2024
48501ef
outline for ast_to_ast updates
sethsabar Apr 10, 2024
29f63ff
ast_to_ast updates complete but untested
sethsabar Apr 10, 2024
9b2a9a2
some testing on ast_to_ast changes
sethsabar Apr 10, 2024
963c6a9
outline for ast_to_ir
sethsabar Apr 11, 2024
1ea0b9c
some work on ast_to_ir
sethsabar Apr 11, 2024
ed8f1a0
fixed bash mode not propogating to JIT engine
sethsabar Apr 13, 2024
e861a45
update requirements for actions tests
sethsabar Apr 13, 2024
9218ec0
minor bug fixes and new bash testing script
sethsabar Apr 15, 2024
ca85a45
make changes to test scripts to support bash
sethsabar Apr 15, 2024
958a355
some work on the bash testing
sethsabar Apr 19, 2024
164ed4b
Add bash tests
sethsabar Apr 26, 2024
d73a0cb
start work on benchmarking
sethsabar Apr 27, 2024
3da7bd2
updates to the bash benchmark routine
sethsabar May 1, 2024
dbeaaaa
bug fixed with echo ast, crazy bash speed ups now
sethsabar May 2, 2024
058fd15
getting ready to run evaluations
sethsabar May 2, 2024
46b8559
bug fix to attain more speedups
sethsabar May 3, 2024
57187d8
add another benchmark script
sethsabar May 6, 2024
2610376
nlp test suite
sethsabar May 7, 2024
e48ddbf
bash evaluation script
sethsabar May 7, 2024
a44723c
Merge branch 'main' of https://github.com/sethsabar/pash into bash-merge
BolunThompson Dec 12, 2024
e817bb3
Fix: python 3.8 compat
BolunThompson Dec 8, 2024
3050c4e
Split up big function
BolunThompson Dec 8, 2024
b430454
Fix tmpdir in tests
BolunThompson Dec 15, 2024
c40fa53
Add 'encoding="utf-8"' to open calls for writing
BolunThompson Dec 15, 2024
3b238c7
Lint: black bash changes
BolunThompson Dec 15, 2024
21576ce
Add comments
BolunThompson Dec 15, 2024
b550e15
Update shasta version
BolunThompson Dec 15, 2024
424bb86
Update libbash version
BolunThompson Dec 17, 2024
75891c3
Fix version specifier in requirements
BolunThompson Dec 21, 2024
441020a
Rerun CI
BolunThompson Dec 22, 2024
3c2606b
Run bash and dash tests together
BolunThompson Dec 26, 2024
89bfc84
Fix typo
BolunThompson Dec 26, 2024
190f03e
Fix accidential merge removal
BolunThompson Dec 29, 2024
a068662
Add bash server expansion from sh_expand
BolunThompson Jan 11, 2025
360fc2e
Add bash evaluation tests to skip
BolunThompson Jan 22, 2025
9f0ad8b
Minor test fix
BolunThompson Jan 23, 2025
7b3cb4a
Merge remote-tracking branch 'upstream/future' into bash-merge
BolunThompson Jan 23, 2025
e957f08
Remove failing bash evaluation tests
BolunThompson Jan 23, 2025
eedd28e
Remove test that fails only in CI
BolunThompson Jan 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
307 changes: 261 additions & 46 deletions compiler/ast_to_ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,25 @@
from shasta.ast_node import *
from sh_expand.expand import expand_command, ExpansionState

import config
from sh_expand.bash_expand import (
BashExpansionState,
expand_command as expand_command_bash,
)
from shell_ast.ast_util import *
from ir import *
from util import *
from parse import from_ast_objects_to_shell

from custom_error import *

BASH_MODE = os.environ.get("pash_shell") == "bash"
if BASH_MODE:
# doesn't need to be kept alive accross invocations
# but should be faster to avoid creating new processes

# too verbose to keep on with -d 1 (TODO: Get -d 2 working here)
BASH_EXP_STATE = BashExpansionState(temp_dir=config.PASH_TMP_PREFIX, debug=False)
## TODO: Separate the ir stuff to the bare minimum and
## try to move this to the shell_ast folder.

Expand Down Expand Up @@ -71,6 +83,67 @@
ast_node, fileIdGen, config
)
),
# Bash only:
"Not": (
lambda fileIdGen, config: lambda ast_node: compile_node_not(
ast_node, fileIdGen, config
)
),
"Defun": (
lambda fileIdGen, config: lambda ast_node: compile_node_defun(
ast_node, fileIdGen, config
)
),
"While": (
lambda fileIdGen, config: lambda ast_node: compile_node_while(
ast_node, fileIdGen, config
)
),
"If": (
lambda fileIdGen, config: lambda ast_node: compile_node_if(
ast_node, fileIdGen, config
)
),
"Case": (
lambda fileIdGen, config: lambda ast_node: compile_node_case(
ast_node, fileIdGen, config
)
),
"Select": (
lambda fileIdGen, config: lambda ast_node: compile_node_select(
ast_node, fileIdGen, config
)
),
"Arith": (
lambda fileIdGen, config: lambda ast_node: compile_node_arith(
ast_node, fileIdGen, config
)
),
"Cond": (
lambda fileIdGen, config: lambda ast_node: compile_node_cond(
ast_node, fileIdGen, config
)
),
"ArithFor": (
lambda fileIdGen, config: lambda ast_node: compile_node_arith_for(
ast_node, fileIdGen, config
)
),
"Coproc": (
lambda fileIdGen, config: lambda ast_node: compile_node_coproc(
ast_node, fileIdGen, config
)
),
"Time": (
lambda fileIdGen, config: lambda ast_node: compile_node_time(
ast_node, fileIdGen, config
)
),
"Group": (
lambda fileIdGen, config: lambda ast_node: compile_node_group(
ast_node, fileIdGen, config
)
),
}


Expand All @@ -85,9 +158,18 @@ def compile_asts(ast_objects: "list[AstNode]", fileIdGen, config):
## Compile subtrees of the AST to out intermediate representation
## KK 2023-05-25: Would we ever want to pass this state to the expansion
## of the next object? I don't think so.
exp_state = ExpansionState(config["shell_variables"])
expanded_ast = expand_command(ast_object, exp_state)
# log("Expanded:", expanded_ast)
if BASH_MODE:
if not BASH_EXP_STATE.is_open:
BASH_EXP_STATE.open()
expanded_ast = expand_command_bash(
ast_object,
BASH_EXP_STATE,
config["shell_variables_file_path"],
config["shell_variables"],
)
else:
exp_state = ExpansionState(config["shell_variables"])
expanded_ast = expand_command(ast_object, exp_state)
compiled_ast = compile_node(expanded_ast, fileIdGen, config)

# log("Compiled AST:")
Expand Down Expand Up @@ -221,7 +303,7 @@ def compile_node_and_or_semi(ast_node, fileIdGen, config):


def compile_node_redir_subshell(ast_node, fileIdGen, config):
compiled_node = compile_node(ast_node.node, fileIdGen, config)
compiled_node = compile_node(ast_node.body, fileIdGen, config)

if isinstance(compiled_node, IR):
## TODO: I should use the redir list to redirect the files of
Expand Down Expand Up @@ -274,6 +356,172 @@ def compile_node_for(ast_node, fileIdGen, config):
return compiled_ast


# Bash only:


def compile_node_not(ast_node, fileIdGen, config):
ast_node: NotNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName, [compile_node(ast_node.body, fileIdGen, config)]
)
return compiled_ast


def compile_node_defun(ast_node, fileIdGen, config):
ast_node: DefunNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
compile_command_argument(ast_node.name),
compile_node(ast_node.body, fileIdGen, config),
],
)
return compiled_ast


def compile_node_while(ast_node, fileIdGen, config):
ast_node: WhileNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
compile_node(ast_node.test, fileIdGen, config),
compile_node(ast_node.body, fileIdGen, config),
],
)
return compiled_ast


def compile_node_if(ast_node, fileIdGen, config):
ast_node: IfNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
compile_node(ast_node.cond, fileIdGen, config),
compile_node(ast_node.then_b, fileIdGen, config),
(
compile_node(ast_node.else_b, fileIdGen, config)
if ast_node.else_b
else None
),
],
)
return compiled_ast


def compile_command_cases(cases, fileIdGen, config):
compiled_cases = [
[
compile_command_argument(case["cpattern"], fileIdGen, config),
compile_node(case["cbody"], fileIdGen, config),
]
for case in cases
]
return compiled_cases


def compile_node_case(ast_node, fileIdGen, config):
ast_node: CaseNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
compile_command_argument(ast_node.argument, fileIdGen, config),
compile_command_cases(ast_node.cases, fileIdGen, config),
],
)
return compiled_ast


def compile_node_select(ast_node, fileIdGen, config):
ast_node: SelectNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
compile_command_argument(ast_node.variable, fileIdGen, config),
compile_node(ast_node.body, fileIdGen, config),
compile_command_arguments(ast_node.map_list, fileIdGen, config),
],
)
return compiled_ast


def compile_node_arith(ast_node, fileIdGen, config):
ast_node: ArithNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
compile_command_arguments(ast_node.argument, fileIdGen, config),
],
)
return compiled_ast


def compile_node_cond(ast_node, fileIdGen, config):
ast_node: CondNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
ast_node.cond_type,
(
compile_command_argument(ast_node.op, fileIdGen, config)
if ast_node.op
else None
),
compile_node(ast_node.left, fileIdGen, config) if ast_node.left else None,
compile_node(ast_node.right, fileIdGen, config) if ast_node.right else None,
ast_node.invert_return,
],
)
return compiled_ast


def compile_node_arith_for(ast_node, fileIdGen, config):
ast_node: ArithForNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
ast_node.line_number,
compile_command_argument(ast_node.init, fileIdGen, config),
compile_command_argument(ast_node.cond, fileIdGen, config),
compile_command_argument(ast_node.step, fileIdGen, config),
compile_node(ast_node.action, fileIdGen, config),
],
)
return compiled_ast


def compile_node_coproc(ast_node, fileIdGen, config):
ast_node: CoprocNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName,
[
compile_command_argument(ast_node.name, fileIdGen, config),
compile_node(ast_node.body, fileIdGen, config),
],
)
return compiled_ast


def compile_node_time(ast_node, fileIdGen, config):
ast_node: TimeNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName, [compile_node(ast_node.command, fileIdGen, config)]
)
return compiled_ast


def compile_node_group(ast_node, fileIdGen, config):
ast_node: GroupNode = ast_node
compiled_ast = make_kv(
type(ast_node).NodeName, [compile_node(ast_node.body, fileIdGen, config)]
)
return compiled_ast


## This function checks if we should expand an arg_char
##
## It has a dual purpose:
Expand Down Expand Up @@ -319,43 +567,6 @@ def parse_string_to_arguments(arg_char_string):
return string_to_arguments(arg_char_string)


## TODO: Use "pash_input_args" when expanding in place of normal arguments.
def naive_expand(argument, config):
## config contains a dictionary with:
## - all variables, their types, and values in 'shell_variables'
## - the name of a file that contains them in 'shell_variables_file_path'
# log(config['shell_variables'])
# log(config['shell_variables_file_path'])

## Create an AST node that "echo"s the argument
echo_asts = make_echo_ast(argument, config["shell_variables_file_path"])

## Execute the echo AST by unparsing it to shell
## and calling bash
expanded_string = execute_shell_asts(echo_asts)

log("Argument:", argument, "was expanded to:", expanded_string)

## Parse the expanded string back to an arg_char
expanded_arguments = parse_string_to_arguments(expanded_string)

## TODO: Handle any errors
# log(expanded_arg_char)
return expanded_arguments


## This function expands an arg_char.
## At the moment it is pretty inefficient as it serves as a prototype.
##
## TODO: At the moment this has the issue that a command that has the words which we want to expand
## might have assignments of its own, therefore requiring that we use them to properly expand.
def expand_command_argument(argument, config):
new_arguments = [argument]
if should_expand_argument(argument):
new_arguments = naive_expand(argument, config)
return new_arguments


## This function compiles an arg char by recursing if it contains quotes or command substitution.
##
## It is currently being extended to also expand any arguments that are safe to expand.
Expand Down Expand Up @@ -393,19 +604,23 @@ def compile_command_arguments(arguments, fileIdGen, config):
return compiled_arguments


## Compiles the value assigned to a variable using the command argument rules.
## TODO: Is that the correct way to handle them?
def compile_assignment(assignment, fileIdGen, config):
assignment.val = compile_command_argument(assignment.val, fileIdGen, config)
return assignment


def compile_assignments(assignments, fileIdGen, config):
compiled_assignments = [
[assignment[0], compile_command_argument(assignment[1], fileIdGen, config)]
compile_assignment(assignment, fileIdGen, config)
for assignment in assignments
]
return compiled_assignments


def compile_redirection(redirection, fileIdGen, config):
file_arg = compile_command_argument(redirection.arg, fileIdGen, config)
redirection.arg = file_arg
if redirection.arg is not None:
file_arg = compile_command_argument(redirection.arg, fileIdGen, config)
redirection.arg = file_arg
return redirection


Expand Down
6 changes: 6 additions & 0 deletions compiler/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ def __init__(self, *args, **kwargs):
help="(experimental) prints commands and their arguments as they execute",
action="store_true",
)
self.add_argument(
"--bash",
help="(experimental) interpret the input as a bash script file",
action="store_true",
)

self.set_defaults(preprocess_mode="pash")


Expand Down
Loading
Loading