Skip to content

Commit

Permalink
implement nested vars
Browse files Browse the repository at this point in the history
  • Loading branch information
voschezang committed Dec 31, 2023
1 parent 21e4493 commit acb6c0e
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 28 deletions.
4 changes: 2 additions & 2 deletions SHELL_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,14 @@ range 10 >>= math 10 * $ + 1 |> reduce sum
```diff
- SELECT name FROM users INNER JOIN documents ON users.id == document.owner
+ {users | users.id in {documents.owner}} >>= show $1.name
+ {users | users.id in {documents.owner}} >>= show $.users.name
```
*"Show documents of each user"*
```diff
- SELECT users.name, documents.name FROM users LEFT JOIN documents ON users.id == document.owner
+ { users documents | users.id = documents.owner } >>= show $1.email $2.name
+ { users documents | users.id = documents.owner } >>= show $.u.email $.d.name
```
Note the similarity to ranges
Expand Down
4 changes: 2 additions & 2 deletions src/mash/shell/ast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from mash.shell.ast.infix import Assign, BashPipe, BinaryExpression, LogicExpression, Map, Pipe
from mash.shell.ast.node import Indent, Math, Node, Return, Shell
from mash.shell.ast.nodes import Lines, Nodes, Terms
from mash.shell.ast.term import Method, Quoted, Term, Variable, PositionalVariable, Word
from mash.shell.ast.term import Method, Quoted, Term, Variable, NestedVariable, PositionalVariable, Word

classes = (Node, Nodes, Term, Terms, Lines, Indent,
Assign, BashPipe, BinaryExpression, LogicExpression,
FunctionDefinition, InlineFunctionDefinition, SetDefinition,
Map, Math, Pipe, Return, Shell,
Word, Method, Quoted, Variable, PositionalVariable)
Word, Method, Quoted, Variable, PositionalVariable, NestedVariable)

conditions = (If, IfThen, Then,
ElseCondition, Else, ElseIf, ElseIfThen,
Expand Down
26 changes: 13 additions & 13 deletions src/mash/shell/ast/infix.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,12 @@ def run(self, prev_result='', shell: BaseShell = None, lazy=False):
prev = shell.run_commands(self.lhs, prev_result, run=not lazy)

if str(prev).strip() == '' and shell._last_results:
prev = shell._last_results
shell.env[LAST_RESULTS] = []
# if isinstance(prev, list):
# for item in prev:
# shell.env[POSITIONALS] = prev

# SMELL
prev = shell._last_results.pop(shell._last_results_index)
if shell._last_results_index > 0:
shell._last_results_index -= 1
else:
prev = str(prev)

rhs = self.rhs
if isinstance(rhs, str) or isinstance(rhs, Term):
Expand All @@ -201,17 +201,17 @@ def map(command, values: str, shell, delimiter='\n') -> Iterable:
# monadic bind
# https://en.wikipedia.org/wiki/Monad_(functional_programming)

try:
items = shell.parse(str(values)).values
except ShellSyntaxError:
items = [Quoted(values)]
if isinstance(values, str):
try:
items = shell.parse(values).values
except ShellSyntaxError:
items = [Quoted(values)]
else:
items = values

results = []
for i, item in enumerate(items):
shell.env[LAST_RESULTS_INDEX] = i
if isinstance(values, list):
shell.env[POSITIONALS] = values

results.append(shell.run_commands(command, item, run=True))

shell.env[LAST_RESULTS_INDEX] = 0
Expand Down
31 changes: 28 additions & 3 deletions src/mash/shell/ast/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from mash.shell.ast.node import Node
from mash.shell.base import POSITIONALS, BaseShell
from mash.shell.grammer.parse_functions import expand_variables
from mash.util import quote_all
from mash.util import has_method, quote_all


class Term(Node):
Expand All @@ -37,6 +37,11 @@ def run_terms(items, prev_result='', shell=None, lazy=False):
if '$' in items:
wildcard_value = prev_result
prev_result = ''

for item in items:
if isinstance(item, NestedVariable):
wildcard_value = prev_result
prev_result = ''

if items[0] == '?':
if len(items) == 1:
Expand All @@ -46,6 +51,11 @@ def run_terms(items, prev_result='', shell=None, lazy=False):
line = ' '.join(['?'] + result)

return shell.onecmd_raw(line, prev_result)

items = items.copy()
for i, item in enumerate(items):
if isinstance(item, NestedVariable):
items[i] = item.expand(wildcard_value)

items = list(expand_variables(items, shell.env,
shell.completenames_options,
Expand Down Expand Up @@ -124,9 +134,24 @@ def run(self, prev_result='', shell: BaseShell = None, lazy=False):

return super().run(prev_result, shell, lazy)


class NestedVariable(Term):
def __init__(self, keys: list):
self.keys = keys
data = '$.' + '.'.join(keys)
super().__init__(data)

def expand(self, data):
for k in self.keys:
data = data[k]

return data


class PositionalVariable(Term):
# TODO remove unused class
def __init__(self, i: int, keys: list):
self.i = i
self.i = int(i)
self.keys = keys

data = '$' + '.'.join([str(i)] + keys)
Expand All @@ -146,4 +171,4 @@ def run(self, prev_result='', shell: BaseShell = None, lazy=False):

return obj

return super().run(prev_result, shell, lazy)
return super().run(prev_result, shell, lazy)
2 changes: 1 addition & 1 deletion src/mash/shell/grammer/parse_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from mash.shell.grammer import literals
from mash.shell.grammer.literals import FALSE, TRUE
from mash.shell.errors import ShellError
from mash.util import crop, is_globbable, is_valid_method_name, match_words, quote, quote_all, glob
from mash.util import crop, has_method, is_globbable, is_valid_method_name, match_words, quote, quote_all, glob


def expand_variables(terms: List[str], env: dict,
Expand Down
10 changes: 8 additions & 2 deletions src/mash/shell/grammer/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@
from ply import yacc
from mash.shell.ast import Assign, BashPipe, BinaryExpression, \
Else, ElseIf, ElseIfThen, FunctionDefinition, If, IfThen, IfThenElse, Then, \
PositionalVariable, Indent, InlineFunctionDefinition, Lines, LogicExpression, \
Map, Math, Method, Pipe, Quoted, Return, Shell, SetDefinition, Terms, Variable, Word
NestedVariable, PositionalVariable, Variable, \
Indent, InlineFunctionDefinition, Lines, LogicExpression, \
Map, Math, Method, Pipe, Quoted, Return, Shell, SetDefinition, Terms, Word
from mash.shell.grammer.tokenizer import main, tokens
from mash.shell.grammer.parse_functions import indent_width
from mash.shell.errors import ShellSyntaxError
Expand Down Expand Up @@ -324,6 +325,11 @@ def p_value_method(p):
'method : METHOD'
p[0] = Method(p[1])

def p_value_nested_variable(p):
'value : NESTED_VARIABLE'
values = p[1].split('.')[1:]
p[0] = NestedVariable(values)

def p_value_positional_variable(p):
'value : POSITIONAL_VARIABLE'
k, *values = p[1].split('.')
Expand Down
2 changes: 2 additions & 0 deletions src/mash/shell/grammer/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
'DOUBLE_QUOTED_STRING', # "a 'b' c"
'SINGLE_QUOTED_STRING', # 'a\'bc'

'NESTED_VARIABLE',
'POSITIONAL_VARIABLE',
'METHOD', # some_method_V1
'SPECIAL', # $
Expand Down Expand Up @@ -55,6 +56,7 @@ def main():
t_DEFINE_FUNCTION = r':'

t_SPECIAL = r'\$'
t_NESTED_VARIABLE = r'\$(\.[a-zA-Z_0-9]+)+'
t_POSITIONAL_VARIABLE = r'\$[\d+](\.[a-zA-Z_0-9]+)*'
t_VARIABLE = r'\$[a-zA-Z_][a-zA-Z_0-9]*'

Expand Down
7 changes: 6 additions & 1 deletion test/shell/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
ElseIf, ElseIfThen, FunctionDefinition, If, IfThen, IfThenElse,
InlineFunctionDefinition, SetDefinition,
Lines, Map, Math, Method, Pipe, Return,
PositionalVariable, Variable,
NestedVariable, PositionalVariable, Variable,
Terms, Word)


Expand Down Expand Up @@ -107,6 +107,11 @@ def test_parse_variable():
assert result.values[0].type == 'term'
assert isinstance(result.values[1], Variable)

def test_parse_nested_variable():
result = parse_line('$.inner.x')
assert isinstance(result, Terms)
assert isinstance(result.values[0], NestedVariable)
assert result.values[0].keys == ['inner', 'x']

def test_parse_positional_variable():
result = parse_line('$0 $1.inner.x')
Expand Down
9 changes: 5 additions & 4 deletions test/shell/test_shell_rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ def test_rest_client_standard_set():
assert len(shell._last_results[0]) > 3
result = catch_output(r'x <- {users}', shell=shell)
assert result == ''
result = catch_output(r'{users} >>= show $1.id', shell=shell)
0
# TODO add assertions
# assert '1001' in result
result = catch_output(r'{users} >>= echo $.users.email', shell=shell)
users = result.splitlines()
assert len(users) == 10
assert 'name.0@company.com' in users
assert 'name.1@company.com' in users

def test_rest_client_filter_set():
for init in (init_explicit_client, init_implicit_client):
Expand Down

0 comments on commit acb6c0e

Please sign in to comment.