def reduce_NodeName_LPAREN_OptFuncArgList_RPAREN(self, *kids): module = kids[0].val.module func_name = kids[0].val.name name = func_name if not module else (module, func_name) last_named_seen = None args = [] kwargs = {} for argname, argname_ctx, arg in kids[2].val: if argname is not None: if argname in kwargs: raise EdgeQLSyntaxError( f"duplicate named argument `{argname}`", context=argname_ctx) last_named_seen = argname kwargs[argname] = arg else: if last_named_seen is not None: raise EdgeQLSyntaxError( f"positional argument after named " f"argument `{last_named_seen}`", context=arg.context) args.append(arg) self.val = qlast.FunctionCall(func=name, args=args, kwargs=kwargs)
def reduce_ARGUMENT(self, dp): if dp.val[1].isdigit(): raise EdgeQLSyntaxError(f'numeric parameters are not supported', context=dp.context) else: raise EdgeQLSyntaxError( f"function parameters do not need a $ prefix, " f"rewrite as '{dp.val[1:]}'", context=dp.context)
def reduce_ARGUMENT_ASSIGN_Expr(self, *kids): if kids[0].val[1].isdigit(): raise EdgeQLSyntaxError( f"numeric named arguments are not supported", context=kids[0].context) else: raise EdgeQLSyntaxError( f"named arguments do not need a '$' prefix, " f"rewrite as '{kids[0].val[1:]} := ...'", context=kids[0].context)
def _process_function_body(self, block): props = {} commands = [] code = None language = qlast.Language.SQL from_expr = False from_function = None for node in block.val: if isinstance(node, qlast.FunctionCode): if node.from_function: if from_function is not None: raise EdgeQLSyntaxError( 'more than one FROM FUNCTION clause', context=node.context) from_function = node.from_function elif node.code: if code is not None: raise EdgeQLSyntaxError( 'more than one FROM <code> clause', context=node.context) code = node.code language = node.language else: # FROM SQL EXPRESSION from_expr = True else: commands.append(node) if (code is None and from_function is None and not from_expr): raise EdgeQLSyntaxError( 'CREATE FUNCTION requires at least one FROM clause', context=block.context) else: if from_expr and (from_function or code): raise EdgeQLSyntaxError( 'FROM SQL EXPRESSION is mutually exclusive with other ' 'FROM variants', context=block.context) props['code'] = qlast.FunctionCode( language=language, from_function=from_function, from_expr=from_expr, code=code, ) if commands: props['commands'] = commands return props
def reduce_Insert(self, *kids): r'%reduce INSERT OptionallyAliasedExpr OptUnlessConflictClause' subj = kids[1].val.expr subj_alias = kids[1].val.alias # check that the insert subject is either a path or a shape if isinstance(subj, qlast.Shape): objtype = subj.expr shape = subj.elements else: objtype = subj shape = [] unless_conflict = kids[2].val if not isinstance(objtype, qlast.Path): raise EdgeQLSyntaxError( "insert expression must be an object type reference", context=subj.context) self.val = qlast.InsertQuery( subject=objtype, subject_alias=subj_alias, shape=shape, unless_conflict=unless_conflict, )
def _float_to_path(self, token, context): from edb.schema import pointers as s_pointers # make sure that the float is of the type 0.1 parts = token.val.split('.') if not (len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit()): raise EdgeQLSyntaxError(f"Unexpected {token.val!r}", context=token.context) # context for the AST is established manually here return [ qlast.Ptr( ptr=qlast.ObjectRef( name=parts[0], context=token.context, ), direction=s_pointers.PointerDirection.Outbound, context=context, ), qlast.Ptr( ptr=qlast.ObjectRef( name=parts[1], context=token.context, ), direction=s_pointers.PointerDirection.Outbound, context=token.context, ) ]
def _parse_language(node): try: return qlast.Language(node.val.upper()) except ValueError: raise EdgeQLSyntaxError( f'{node.val} is not a valid language', context=node.context) from None
def parse(self, input): try: self.reset_parser(input) mod = self.get_parser_spec_module() tok = self.lexer.token() while tok: token = self.process_lex_token(mod, tok) if token is not None: self.parser.token(token) tok = self.lexer.token() self.parser.eoi() except TokenizerError as e: message, position = e.args hint = _derive_hint(input, message, position) raise EdgeQLSyntaxError(message, context=self.context(pos=position), hint=hint) from e except parsing.SyntaxError as e: raise self.get_exception(e, context=self.context(tok), token=tok) from e except ParserError as e: raise self.get_exception(e, context=e.context) from e except lexer.LexError as e: raise self.get_exception(e, context=self.context(None)) from e return self.parser.start[0].val
def reduce_FROM_Identifier_CAST(self, *kids): lang = commondl._parse_language(kids[1]) if lang != qlast.Language.SQL: raise EdgeQLSyntaxError( f'{lang} language is not supported in FROM CAST clause', context=kids[1].context) from None self.val = qlast.CastCode(language=lang, from_cast=True)
def reduce_FROM_Identifier_EXPRESSION(self, *kids): lang = _parse_language(kids[1]) if lang != qlast.Language.SQL: raise EdgeQLSyntaxError( f'{lang} language is not supported in FROM clause', context=kids[1].context) from None self.val = qlast.FunctionCode(language=lang)
def reduce_LPAREN_FuncDeclArgList_RPAREN(self, *kids): args = kids[1].val last_pos_default_arg = None last_named_arg = None variadic_arg = None names = set() for arg in args: if arg.name in names: raise EdgeQLSyntaxError( f'duplicate parameter name `{arg.name}`', context=arg.context) names.add(arg.name) if arg.kind is qltypes.ParameterKind.VARIADIC: if variadic_arg is not None: raise EdgeQLSyntaxError('more than one variadic argument', context=arg.context) elif last_named_arg is not None: raise EdgeQLSyntaxError( f'NAMED ONLY argument `{last_named_arg.name}` ' f'before VARIADIC argument `{arg.name}`', context=last_named_arg.context) else: variadic_arg = arg if arg.default is not None: raise EdgeQLSyntaxError( f'VARIADIC argument `{arg.name}` ' f'cannot have a default value', context=arg.context) elif arg.kind is qltypes.ParameterKind.NAMED_ONLY: last_named_arg = arg else: if last_named_arg is not None: raise EdgeQLSyntaxError( f'positional argument `{arg.name}` ' f'follows NAMED ONLY argument `{last_named_arg.name}`', context=arg.context) if variadic_arg is not None: raise EdgeQLSyntaxError( f'positional argument `{arg.name}` ' f'follows VARIADIC argument `{variadic_arg.name}`', context=arg.context) if arg.kind is qltypes.ParameterKind.POSITIONAL: if arg.default is None: if last_pos_default_arg is not None: raise EdgeQLSyntaxError( f'positional argument `{arg.name}` without ' f'default follows positional argument ' f'`{last_pos_default_arg.name}` with default', context=arg.context) else: last_pos_default_arg = arg self.val = args
def reduce_FROM_Identifier_BaseStringConstant(self, *kids): lang = commondl._parse_language(kids[1]) if lang not in {qlast.Language.SQL, qlast.Language.EdgeQL}: raise EdgeQLSyntaxError( f'{lang} language is not supported in FROM clause', context=kids[1].context) from None self.val = qlast.CastCode(language=lang, code=kids[2].val.value)
def reduce_FROM_Identifier_FUNCTION_BaseStringConstant(self, *kids): lang = _parse_language(kids[1]) if lang != qlast.Language.SQL: raise EdgeQLSyntaxError( f'{lang} language is not supported in FROM FUNCTION clause', context=kids[1].context) from None self.val = qlast.FunctionCode(language=lang, from_function=kids[3].val.value)
def reduce_RSCONST(self, str_tok): match = lexutils.VALID_RAW_STRING_RE.match(str_tok.val) if not match: raise EdgeQLSyntaxError( f"invalid raw string literal", context=str_tok.context) quote = match.group('Q') val = match.group('body') self.val = qlast.RawStringConstant(value=val, quote=quote)
def reduce_SCONST(self, str_tok): match = lexutils.VALID_STRING_RE.match(str_tok.val) if not match: raise EdgeQLSyntaxError( f"invalid string literal", context=str_tok.context) if match.group('err_esc'): raise EdgeQLSyntaxError( f"invalid string literal: invalid escape sequence " f"'{match.group('err_esc')}'", context=str_tok.context) quote = match.group('Q') val = match.group('body') # handle line continuations val = re.sub(r'\\\n', '', val) self.val = qlast.StringConstant(value=val, quote=quote)
def _process_function_body(self, block): props = _process_commands(block.val) function_code = props.get('function_code') if (not function_code or (function_code.code is None and function_code.from_function is None and not function_code.from_expr)): raise EdgeQLSyntaxError( 'CREATE FUNCTION requires at least one FROM clause', context=block.context) else: if function_code.from_expr and (function_code.from_function or function_code.code): raise EdgeQLSyntaxError( 'FROM SQL EXPRESSION is mutually exclusive with other ' 'FROM variants', context=block.context) return props
def reduce_BCONST(self, bytes_tok): val = bytes_tok.val match = lexutils.VALID_BYTES_RE.match(val) if not match: raise EdgeQLSyntaxError(f"invalid bytes literal", context=bytes_tok.context) if match.group('err_esc'): raise EdgeQLSyntaxError( f"invalid bytes literal: invalid escape sequence " f"'{match.group('err_esc')}'", context=bytes_tok.context) if match.group('err'): raise EdgeQLSyntaxError( f"invalid bytes literal: character '{match.group('err')}' " f"is outside of the ASCII range", context=bytes_tok.context) self.val = qlast.BytesConstant(value=match.group('body'), quote=match.group('BQ'))
def _validate_declarations( declarations: Sequence[Union[qlast.ModuleDeclaration, qlast.DDLCommand]] ) -> None: # Check that top-level declarations either use fully-qualified # names or are module blocks. for decl in declarations: if (not isinstance(decl, qlast.ModuleDeclaration) and decl.name.module is None): raise EdgeQLSyntaxError( "only fully-qualified name is allowed in " "top-level declaration", context=decl.name.context)
def reduce_FROM_Identifier_OPERATOR_BaseStringConstant(self, *kids): lang = commondl._parse_language(kids[1]) if lang != qlast.Language.SQL: raise EdgeQLSyntaxError( f'{lang} language is not supported in FROM OPERATOR clause', context=kids[1].context) from None sql_operator = kids[3].val.value m = re.match(r'([^(]+)(?:\((\w*(?:,\s*\w*)*)\))?', sql_operator) if not m: raise EdgeQLSyntaxError( f'invalid syntax for FROM OPERATOR clause', context=kids[3].context) from None sql_operator = (m.group(1),) if m.group(2): operands = tuple(op.strip() for op in m.group(2).split(',')) sql_operator += operands self.val = qlast.OperatorCode( language=lang, from_operator=sql_operator)
def reduce_ReservedKeyword(self, *kids): name = kids[0].val if name[:2] == '__' and name[-2:] == '__': # There are a few reserved keywords like __std__ and __subject__ # that can be used in paths but are prohibited to be used # anywhere else. So just as the tokenizer prohibits using # __names__ in general, we enforce the rule here for the # few remaining reserved __keywords__. raise EdgeQLSyntaxError( "identifiers surrounded by double underscores are forbidden", context=kids[0].context) self.val = name
def reduce_VERSION_BaseStringConstant(self, *kids): version = kids[1].val try: verutils.parse_version(version.value) except ValueError: raise EdgeQLSyntaxError( 'invalid extension version format', details='Expected a SemVer-compatible format.', context=version.context, ) from None self.val = version
def validate_subtype_list(self, lst): has_nonstrval = has_strval = has_items = False for el in lst.val: if isinstance(el, qlast.TypeExprLiteral): has_strval = True elif isinstance(el, qlast.TypeName): if el.name: has_items = True else: has_nonstrval = True if (has_nonstrval or has_items) and has_strval: # Prohibit cases like `tuple<a: int64, 'aaaa'>` and # `enum<bbbb, 'aaaa'>` raise EdgeQLSyntaxError( "mixing string type literals and type names is not supported", context=lst.context) if has_items and has_nonstrval: # Prohibit cases like `tuple<a: int64, int32>` raise EdgeQLSyntaxError( "mixing named and unnamed subtype declarations " "is not supported", context=lst.context)
def reduce_SCONST(self, str_tok): match = lexutils.VALID_STRING_RE.match(str_tok.val) if not match: raise EdgeQLSyntaxError(f"invalid string literal", context=str_tok.context) if match.group('err_esc'): raise EdgeQLSyntaxError( f"invalid string literal: invalid escape sequence " f"'{match.group('err_esc')}'", context=str_tok.context) elif match.group('err_cont'): raise EdgeQLSyntaxError( f"invalid string literal: invalid line continuation", hint="newline has to immediately follow '\\'", context=str_tok.context) quote = match.group('Q') val = match.group('body') # collapse the whitespace after a line continuation val = lexutils.collapse_newline_whitespace(val) self.val = qlast.StringConstant(value=val, quote=quote)
def reduce_OptParameterKind_FuncDeclArgName_OptDefault(self, *kids): raise EdgeQLSyntaxError( f'missing type declaration for the `{kids[1].val}` parameter', context=kids[1].context)
def reduce_DOLLAR_ICONST(self, dk, di): raise EdgeQLSyntaxError(f'numeric parameters are not supported', context=dk.context)
def reduce_DOLLAR_AnyIdentifier(self, dk, dp): raise EdgeQLSyntaxError( f"function parameters do not need a $ prefix, " f"rewrite as '{dp.val}'", context=dk.context)
def reduce_Shape(self, *kids): raise EdgeQLSyntaxError(f"Missing ':' before '{{' in a sub-shape", context=kids[0].context)
def _process_function_body(self, block, *, optional_using: bool = False): props: typing.Dict[str, typing.Any] = {} commands = [] code = None nativecode = None language = qlast.Language.EdgeQL from_expr = False from_function = None for node in block.val: if isinstance(node, qlast.FunctionCode): if node.from_function: if from_function is not None: raise EdgeQLSyntaxError( 'more than one USING FUNCTION clause', context=node.context) from_function = node.from_function language = qlast.Language.SQL elif node.nativecode: if code is not None or nativecode is not None: raise EdgeQLSyntaxError( 'more than one USING <code> clause', context=node.context) nativecode = node.nativecode language = node.language elif node.code: if code is not None or nativecode is not None: raise EdgeQLSyntaxError( 'more than one USING <code> clause', context=node.context) code = node.code language = node.language else: # USING SQL EXPRESSION from_expr = True language = qlast.Language.SQL else: commands.append(node) if (nativecode is None and code is None and from_function is None and not from_expr and not optional_using): raise EdgeQLSyntaxError('missing a USING clause', context=block.context) else: if from_expr and (from_function or code): raise EdgeQLSyntaxError( 'USING SQL EXPRESSION is mutually exclusive with other ' 'USING variants', context=block.context) props['code'] = qlast.FunctionCode( language=language, from_function=from_function, from_expr=from_expr, code=code, ) props['nativecode'] = nativecode if commands: props['commands'] = commands return props
def reduce_DOLLAR_ICONST_ASSIGN_Expr(self, *kids): raise EdgeQLSyntaxError( f"numeric named arguments are not supported", context=kids[0].context)
def reduce_DOLLAR_AnyIdentifier_ASSIGN_Expr(self, *kids): raise EdgeQLSyntaxError( f"named arguments do not need a '$' prefix: " f"rewrite as '{kids[1].val} := ...'", context=kids[0].context)