diff --git a/bashlex/ast.py b/bashlex/ast.py index 3d69edf9..fda85ba9 100644 --- a/bashlex/ast.py +++ b/bashlex/ast.py @@ -92,6 +92,26 @@ def visit(self, n): dochild = self._visitnode(n, n.command) if dochild is None or dochild: self.visit(n.command) + elif k == 'case': + dochild = self._visitnode(n, n.parts) + if dochild is None or dochild: + for child in n.parts: + self.visit(child) + elif k == 'pattern_list': + dochild = self._visitnode(n, n.parts, None) + if dochild is None or dochild: + for child in n.parts: + self.visit(child) + elif k == 'case_clause_sequence': + dochild = self._visitnode(n, n.parts) + if dochild is None or dochild: + for child in n.parts: + self.visit(child) + elif k == 'pattern': + dochild = self._visitnode(n, n.parts) + if dochild is None or dochild: + for child in n.parts: + self.visit(child) else: raise ValueError('unknown node kind %r' % k) self.visitnodeend(n) @@ -130,6 +150,14 @@ def visitreservedword(self, n, word): pass def visitparameter(self, n, value): pass + def visitcase(self, n, parts): + pass + def visitcase_clause_sequence(self, n, parts): + pass + def visitpattern(self, n, parts): + pass + def visitpattern_list(self, n, parts, action): + pass def visittilde(self, n, value): pass def visitredirect(self, n, input, type, output, heredoc): diff --git a/bashlex/parser.py b/bashlex/parser.py index e6586c24..03c562eb 100644 --- a/bashlex/parser.py +++ b/bashlex/parser.py @@ -293,7 +293,11 @@ def p_case_command(p): '''case_command : CASE WORD newline_list IN newline_list ESAC | CASE WORD newline_list IN case_clause_sequence newline_list ESAC | CASE WORD newline_list IN case_clause ESAC''' - handleNotImplemented(p, 'case command') + parts = _makeparts(p) + p[0] = ast.node(kind='compound', + redirects = [], + list=[ast.node(kind='case', parts=parts, pos=_partsspan(parts))], + pos=_partsspan(parts)) def p_function_def(p): '''function_def : WORD LEFT_PAREN RIGHT_PAREN newline_list function_body @@ -377,14 +381,39 @@ def p_elif_clause(p): def p_case_clause(p): '''case_clause : pattern_list | case_clause_sequence pattern_list''' - handleNotImplemented(p, 'case clause') + + if len(p) == 2: + p[0]=p[1] + else: + p[0] = p[2] + p[0].parts.append(p[1]) def p_pattern_list(p): '''pattern_list : newline_list pattern RIGHT_PAREN compound_list | newline_list pattern RIGHT_PAREN newline_list | newline_list LEFT_PAREN pattern RIGHT_PAREN compound_list | newline_list LEFT_PAREN pattern RIGHT_PAREN newline_list''' - handleNotImplemented(p, 'pattern list') + + parts = [] + action = None + # If we are in cases 1 or 2, we need to construct a reservedwrod node for right parentheses + if p.slice[2].type == "pattern": + patterns = p[2] + parts.extend(patterns) + rparen = ast.node(kind='reservedword', word=p[3], pos = p.lexspan(3)) + parts.append(rparen) + # If we are in cases 3 or 4, we need to construct a reservedword node for left and right parentheses + else: + lparen = ast.node(kind='reservedword', word=p[2], pos=p.lexspan(2)) + patterns = p[3] + rparen = ast.node(kind='reservedword', word=p[4], pos=p.lexspan(4)) + parts.extend([lparen, patterns, rparen]) + # If we are in cases 1 or 3, we need to include the compound_list node at the end + if p.slice[-1].type == "compound_list": + action = p[len(p)-1] + parts.append(action) + p[0] = ast.node(kind="pattern_list", + parts=parts, pos = _partsspan(parts)) def p_case_clause_sequence(p): '''case_clause_sequence : pattern_list SEMI_SEMI @@ -393,12 +422,26 @@ def p_case_clause_sequence(p): | case_clause_sequence pattern_list SEMI_AND | pattern_list SEMI_SEMI_AND | case_clause_sequence pattern_list SEMI_SEMI_AND''' - handleNotImplemented(p, 'case clause') + parts = _makeparts(p) + end = parts[len(parts)-1] + if len(p) == 3: + p[0]=p[1] + p[0].parts.append(end) + else: + p[0] = p[2] + p[0].parts.append(end) + p[0].parts.append(p[1]) def p_pattern(p): '''pattern : WORD | pattern BAR WORD''' - handleNotImplemented(p, 'pattern') + + parserobj = p.context + if len(p) == 2: + p[0] = [_expandword(parserobj, p.slice[1])] + else: + p[0] = p[1] + p[0].append(_expandword(parserobj, p.slice[3])) def p_list(p): '''list : newline_list list0''' diff --git a/bashlex/subst.py b/bashlex/subst.py index 2df60235..2f08e914 100644 --- a/bashlex/subst.py +++ b/bashlex/subst.py @@ -49,7 +49,9 @@ def _parsedolparen(parserobj, base, sindex): def _extractcommandsubst(parserobj, string, sindex, sxcommand=False): if string[sindex] == '(': - raise NotImplementedError('arithmetic expansion') + node, si = _parsedolparen(parserobj, string, sindex) + si += 1 + return ast.node(kind="commandsubstitution", command=node, pos=(sindex-2, si)), si #return _extractdelimitedstring(parserobj, string, sindex, '$(', '(', '(', sxcommand=True) else: node, si = _parsedolparen(parserobj, string, sindex) diff --git a/tests/test_parser.py b/tests/test_parser.py index 22e6758c..556ec8a0 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -67,6 +67,17 @@ def compoundnode(s, *parts, **kwargs): assert not kwargs return ast.node(kind='compound', s=s, list=list(parts), redirects=redirects) +def casenode(s, *parts): + return(ast.node(kind='compound', + redirects=[], + list = [ast.node(kind='case', parts=list(parts), s=s)])) + +def caseclausesequence(s, *parts): + return ast.node(kind='caseclausesequence', parts=list(parts), s=s) + +def patternlistnode(s, *parts): + return ast.node(kind='patternlist', parts=list(parts), s=s) + def procsubnode(s, command): return ast.node(kind='processsubstitution', s=s, command=command) @@ -1103,3 +1114,52 @@ def test_parameter_braces(self): ]) ), ) + + def test_cases(self): + #return + s = """ + case "$1" in + 1) echo foo + ;; + *) + ( + echo bar + ) + ;; + esac + """ + self.assertASTEquals(s, + compoundnode(s, + casenode(s, + reservedwordnode('case', 'case'), + wordnode('$1', + parameternode('1', '1') + ), + reservedwordnode('in', 'in'), + patternlistnode('1) echo foo\n;;\n*)\n(\necho bar\n);;\n', + wordnode('*', '*'), + reservedwordnode(')', ')'), + compoundnode('(\necho bar\n)', + reservedwordnode('(', '('), + commandnode('echo bar', + wordnode('echo', 'echo'), + wordnode('bar', 'bar') + ), + reservedwordnode(')', ')') + ), + reservedwordnode(';;', ';;'), + patternlistnode('1) echo foo\n;;', + wordnode('1', '1'), + reservedwordnode(')', ')'), + commandnode('echo foo\n', + wordnode('echo', 'echo'), + wordnode('foo', 'foo') + ), + reservedwordnode(';;', ';;') + ) + ), + reservedwordnode('esac', 'esac') + ) + ) + ) +