class SubstitutionParser: """Parse input in order to substitute all variables($var).""" parser_path = os_file_path('parsing', 'substitution', 'Substitution.lark') def __init__(self): self.parser = LarkParserLoader.create_parser(self.parser_path) def parse(self, env: Environment, string: str) -> str: """Parse input, substitute variable and return the result.""" try: tree = self.parser.parse(string) transformer = SubstitutionTransformer(env) tree = transformer.transform(tree) reconstructor = Reconstructor(self.parser) return reconstructor.reconstruct(tree) except (UnexpectedCharacters, UnexpectedToken) as e: raise ShellException('[Substitution]Unexpected characters at position %s' % e.pos_in_stream) except LarkError: raise ShellException('[Substitution]Parse error')
class ShellParser: """Parse input string to list of commands.""" parser_path = os_file_path('parsing', 'parser', 'ShellGrammar.lark') def __init__(self, command_factory: CommandFactory): self.parser = LarkParserLoader.create_parser(self.parser_path) self.shell_transformer = ShellTransformer(command_factory) def parse(self, string: str) -> list: """Parse input, return list of commands.""" try: tree = self.get_ast(string) commands = self.shell_transformer.transform(tree) return commands except (UnexpectedCharacters, UnexpectedToken) as e: raise ShellException('[Parser]Unexpected characters at position %s' % e.pos_in_stream) except LarkError: raise ShellException('[Parser]Parse error') def get_ast(self, string: str) -> Tree: """Returns input string AST representation.""" return self.parser.parse(string)
import unittest from parameterized import parameterized from commands.CatCommand import CatCommand from commands.Command import Command from environment.Environment import Environment from files.files_io import os_file_path from test.CommandTest import CommandTest file1 = os_file_path('test', 'test_file_1.txt') file2 = os_file_path('test', 'test_file_2.txt') fileNotExists = os_file_path('test', 'test_file_0.txt') class CatCommandTest(CommandTest): @parameterized.expand([ ('empty', [], '', ''), ('one arg', [file1], '', 'Hello world!'), ('args', [file1, file2], '', 'Hello world!Some string.\nSome other string.'), ('input', [], 'Hello from input', 'Hello from input'), ('input and args', [file1, file2], 'Hello from input', 'Hello world!Some string.\nSome other string.'), ]) def test(self, _, args, input_string, output_string): self.command_test(args, input_string, output_string) def command(self, args: list, environment: Environment) -> Command: return CatCommand(args, environment)
import unittest from parameterized import parameterized from commands.Command import Command from commands.CustomCommand import CustomCommand from environment.Environment import Environment from environment.os_environment import extend_environment from files.files_io import os_file_path from test.CommandTest import CommandTest script = os_file_path('test', 'script.py') class CustomCommandTest(CommandTest): def command(self, args: list, environment: Environment) -> Command: extend_environment(environment) return CustomCommand(args, environment) @parameterized.expand([ ('python', 'python', [script], '', 'Hello world!\n'), ]) def test(self, _, name, args, input_string, output_string): self.command_test([name] + args, input_string, output_string) if __name__ == '__main__': unittest.main()
import io import unittest from parameterized import parameterized from Shell import Shell from files.files_io import os_file_path file1 = os_file_path('test', 'test_file_1.txt') class ShellTest(unittest.TestCase): @parameterized.expand([ ('exit', 'exit\n', ''), ('echo', 'echo "Hello world!"\nexit\n', 'Hello world!\n'), ('substitution', 'x=42\necho "\'$x\'"\nexit\n', "'42'\n"), ('pipe', 'echo hello | wc\nexit\n', 'newlines = 1; words = 1; bytes = 5\n'), ('wc', 'wc test/test_file_1.txt\nexit\n', 'newlines = 1; words = 2; bytes = 12 test/test_file_1.txt\n'), ('cat', 'cat test/test_file_1.txt\nexit\n', 'Hello world!\n'), ('grep', 'grep other test/test_file_2.txt\nexit\n', 'Some other string.\n'), ('wrong', '8 = 3\nexit\n', ''), ('substitution magic', 'x = ex\ny = it\necho $x$y\n$x$y\n', 'exit\n'), ]) def test(self, _, input_string, output_string): input_stream = io.StringIO(input_string) output_stream = io.StringIO() # noinspection PyTypeChecker