def funcargs(self, kids, meta): ''' A list of function parameters (as part of a function definition) ''' newkids = [] names = set() kwfound = False for kid in kids: kid = self._convert_child(kid) newkids.append(kid) if isinstance(kid, s_ast.CallKwarg): name = kid.kids[0].valu kwfound = True # Make sure no repeated kwarg else: name = kid.valu # Make sure no positional follows a kwarg if kwfound: mesg = f"Positional parameter '{name}' follows keyword parameter in definition" raise s_exc.BadSyntax(mesg=mesg, at=meta.start_pos) if name in names: mesg = f"Duplicate parameter '{name}' in function definition" raise s_exc.BadSyntax(mesg=mesg, at=meta.start_pos) names.add(name) return s_ast.FuncArgs(newkids)
def parse_float(text, off): match = floatre.match(text[off:]) if match is None: raise s_exc.BadSyntax(at=off, mesg='Invalid float') s = match.group(0) return float(s), len(s) + off
def raiseOnBadStorm(q): ''' Just enough storm parsing for this test ''' # TODO: Async this and use AsyncMock when Python 3.8+ only f = asyncio.Future() if (q[0] == '[') != (q[-1] == ']'): f.set_exception(s_exc.BadSyntax(mesg='mismatched braces')) else: f.set_result('all good') return f
def exit(self, status=0, message=None): ''' Argparse expects exit() to be a terminal function and not return. As such, this function must raise an exception which will be caught by Cmd.hasValidOpts. ''' self.exited = True if message is not None: self.mesgs.extend(message.split('\n')) raise s_exc.BadSyntax(mesg=message, prog=self.prog, status=status)
def _larkToSynExc(self, e): ''' Convert lark exception to synapse BadSyntax exception ''' mesg = regex.split('[\n!]', str(e))[0] at = len(self.text) if isinstance(e, lark.exceptions.UnexpectedCharacters): expected = sorted(terminalEnglishMap[t] for t in e.allowed) mesg += f'. Expecting one of: {", ".join(expected)}' at = e.pos_in_stream elif isinstance(e, lark.exceptions.UnexpectedEOF): expected = sorted(terminalEnglishMap[t] for t in set(e.expected)) mesg += ' ' + ', '.join(expected) elif isinstance(e, lark.exceptions.VisitError): # Lark unhelpfully wraps an exception raised from AstConverter in a VisitError. Unwrap it. origexc = e.orig_exc if not isinstance(origexc, s_exc.SynErr): raise # pragma: no cover origexc.errinfo['text'] = self.text return s_exc.BadSyntax(**origexc.errinfo) return s_exc.BadSyntax(at=at, text=self.text, mesg=mesg)
def getStormEval(self, runt, name): ''' Construct an evaluator function that takes a path and returns a value. This allows relative / absolute props and variables. ''' if name.startswith('$'): varn = name[1:] def func(path): return path.getVar(varn, defv=None) return func if name.startswith(':'): prop = name[1:] def func(path): return path.node.get(prop) return func if name.startswith('.'): def func(path): return path.node.get(name) return func form = runt.snap.core.model.form(name) if form is not None: def func(path): if path.node.form != form: return None return path.node.ndef[1] return func prop = runt.snap.core.model.prop(name) if prop is not None: def func(path): if path.node.form != prop.form: return None return path.node.get(prop.name) return func mesg = 'Unknown prop/variable syntax' raise s_exc.BadSyntax(mesg=mesg, valu=name)
def _larkToSynExc(self, e): ''' Convert lark exception to synapse badGrammar exception ''' mesg = regex.split('[\n!]', e.args[0])[0] at = len(self.text) if isinstance(e, lark.exceptions.UnexpectedCharacters): mesg += f'. Expecting one of: {", ".join(terminalEnglishMap[t] for t in sorted(set(e.allowed)))}' at = e.pos_in_stream elif isinstance(e, lark.exceptions.ParseError): if mesg == 'Unexpected end of input': mesg += f'. Expecting one of: {", ".join(terminalEnglishMap[t] for t in self._eofParse(e.args[0]))}' return s_exc.BadSyntax(at=at, text=self.text, mesg=mesg)
def funccall(self, kids, meta): kids = self._convert_children(kids) argkids = [] kwargkids = [] kwnames = set() for kid in kids[1:]: if isinstance(kid, s_ast.CallKwarg): name = kid.kids[0].valu if name in kwnames: mesg = f"Duplicate keyword argument '{name}' in function call" raise s_exc.BadSyntax(mesg=mesg, at=meta.start_pos) kwnames.add(name) kwargkids.append(kid) else: if kwargkids: mesg = 'Positional argument follows keyword argument in function call' raise s_exc.BadSyntax(mesg=mesg, at=meta.start_pos) argkids.append(kid) args = s_ast.CallArgs(kids=argkids) kwargs = s_ast.CallKwargs(kids=kwargkids) return s_ast.FuncCall(kids=[kids[0], args, kwargs])
def cmdrargs(self, kids): argv = [] for kid in kids: if isinstance(kid, s_ast.SubQuery): argv.append(kid.text) continue # this one should never happen, but is here in case if isinstance(kid, s_ast.Const): # pragma: no cover argv.append(kid.valu) continue if isinstance(kid, lark.lexer.Token): argv.append(str(kid)) continue # pragma: no cover mesg = f'Unhandled AST node type in cmdrargs: {kid!r}' raise s_exc.BadSyntax(mesg=mesg) return argv
def raiseOnBadStorm(q): ''' Just enough storm parsing for this test ''' if (q[0] == '[') != (q[-1] == ']'): raise s_exc.BadSyntax(mesg='mismatched braces')
def getCmdOpts(self, text): ''' Use the _cmd_syntax def to split/parse/normalize the cmd line. Args: text (str): Command to process. Notes: This is implemented independent of argparse (et al) due to the need for syntax aware argument splitting. Also, allows different split per command type Returns: dict: An opts dictionary. ''' off = 0 _, off = s_grammar.nom(text, off, s_grammar.whites) name, off = s_grammar.meh(text, off, s_grammar.whites) _, off = s_grammar.nom(text, off, s_grammar.whites) opts = {} args = collections.deque( [synt for synt in self._cmd_syntax if not synt[0].startswith('-')]) switches = { synt[0]: synt for synt in self._cmd_syntax if synt[0].startswith('-') } # populate defaults and lists for synt in self._cmd_syntax: snam = synt[0].strip('-') defval = synt[1].get('defval') if defval is not None: opts[snam] = defval if synt[1].get('type') == 'list': opts[snam] = [] def atswitch(t, o): # check if we are at a recognized switch. if not # assume the data is part of regular arguments. if not text.startswith('-', o): return None, o name, x = s_grammar.meh(t, o, s_grammar.whites) swit = switches.get(name) if swit is None: return None, o return swit, x while off < len(text): _, off = s_grammar.nom(text, off, s_grammar.whites) swit, off = atswitch(text, off) if swit is not None: styp = swit[1].get('type', 'flag') snam = swit[0].strip('-') if styp == 'valu': valu, off = s_parser.parse_cmd_string(text, off) opts[snam] = valu elif styp == 'list': valu, off = s_parser.parse_cmd_string(text, off) if not isinstance(valu, list): valu = valu.split(',') opts[snam].extend(valu) elif styp == 'enum': vals = swit[1].get('enum:vals') valu, off = s_parser.parse_cmd_string(text, off) if valu not in vals: raise s_exc.BadSyntax(mesg='%s (%s)' % (swit[0], '|'.join(vals)), text=text) opts[snam] = valu else: opts[snam] = True continue if not args: raise s_exc.BadSyntax(mesg='trailing text: [%s]' % (text[off:], ), text=text) synt = args.popleft() styp = synt[1].get('type', 'valu') # a glob type eats the remainder of the string if styp == 'glob': opts[synt[0]] = text[off:] break # eat the remainder of the string as separate vals if styp == 'list': valu = [] while off < len(text): item, off = s_parser.parse_cmd_string(text, off) valu.append(item) opts[synt[0]] = valu break valu, off = s_parser.parse_cmd_string(text, off) opts[synt[0]] = valu return opts