From 7df4e258bed5349e053f5b2275771cb85724d4d0 Mon Sep 17 00:00:00 2001 From: Nigel Gott <nigel@baserow.io> Date: Fri, 22 Oct 2021 08:37:52 +0000 Subject: [PATCH] Deprecate field_by_id special baserow formula function to greatly simplify... --- .../contrib/database/api/formula/views.py | 2 +- .../contrib/database/application_types.py | 12 +- .../contrib/database/fields/field_types.py | 29 +- .../contrib/database/fields/handler.py | 36 +- .../baserow/contrib/database/fields/models.py | 1 + .../contrib/database/fields/registries.py | 6 +- .../contrib/database/formula/ast/tree.py | 36 +- .../contrib/database/formula/ast/visitors.py | 6 - .../formula/expression_generator/generator.py | 29 +- .../database/formula/parser/ast_mapper.py | 104 +- .../database/formula/parser/exceptions.py | 8 + .../parser/generated/BaserowFormula.interp | 5 +- .../parser/generated/BaserowFormula.py | 1177 +++++++++++------ .../parser/generated/BaserowFormula.tokens | 6 +- .../generated/BaserowFormulaLexer.interp | 10 +- .../parser/generated/BaserowFormulaLexer.py | 876 +++++++----- .../generated/BaserowFormulaLexer.tokens | 6 +- .../generated/BaserowFormulaListener.py | 94 +- .../parser/generated/BaserowFormulaVisitor.py | 53 +- .../contrib/database/formula/parser/parser.py | 35 +- .../parser/replace_field_by_id_with_field.py | 95 -- .../parser/replace_field_with_field_by_id.py | 123 -- .../formula/parser/update_field_names.py | 149 +++ .../database/formula/types/table_typer.py | 216 ++- .../formula/types/typed_field_updater.py | 65 +- .../database/formula/types/visitors.py | 85 +- .../0040_formulafield_remove_field_by_id.py | 209 +++ .../baserow/contrib/database/table/models.py | 4 +- .../contrib/database/trash/trash_types.py | 6 +- .../database/api/fields/test_formula_views.py | 19 +- .../database/field/test_field_types.py | 2 +- .../database/field/test_formula_field_type.py | 52 +- .../formula/test_baserow_formula_results.py | 1 + .../test_replace_field_by_id_with_field.py | 76 -- .../test_replace_field_with_field_by_id.py | 100 -- .../formula/test_update_field_names.py | 207 +++ .../test_remove_field_by_id_migration.py | 106 ++ .../database/table/test_table_models.py | 2 +- .../test_database_application_type.py | 4 +- changelog.md | 1 + docs/guides/formula-technical-guide.md | 17 +- formula/BaserowFormula.g4 | 10 + formula/BaserowFormula.tokens | 6 +- formula/BaserowFormulaLexer.g4 | 9 +- formula/BaserowFormulaLexer.tokens | 6 +- .../components/field/FieldFormulaSubForm.vue | 45 +- .../parser/generated/BaserowFormula.interp | 5 +- .../parser/generated/BaserowFormula.js | 395 ++++-- .../parser/generated/BaserowFormula.tokens | 6 +- .../generated/BaserowFormulaLexer.interp | 10 +- .../parser/generated/BaserowFormulaLexer.js | 822 ++++++------ .../generated/BaserowFormulaLexer.tokens | 6 +- .../generated/BaserowFormulaListener.js | 27 + .../parser/generated/BaserowFormulaVisitor.js | 18 + .../parser/replaceFieldByIdWithField.js | 105 -- .../formula/parser/updateFieldNames.js | 99 -- web-frontend/modules/database/realtime.js | 2 +- .../formula/replaceFieldByIdWithField.spec.js | 74 -- .../database/formula/updateFieldNames.spec.js | 89 -- 59 files changed, 3207 insertions(+), 2597 deletions(-) delete mode 100644 backend/src/baserow/contrib/database/formula/parser/replace_field_by_id_with_field.py delete mode 100644 backend/src/baserow/contrib/database/formula/parser/replace_field_with_field_by_id.py create mode 100644 backend/src/baserow/contrib/database/formula/parser/update_field_names.py create mode 100644 backend/src/baserow/contrib/database/migrations/0040_formulafield_remove_field_by_id.py delete mode 100644 backend/tests/baserow/contrib/database/formula/test_replace_field_by_id_with_field.py delete mode 100644 backend/tests/baserow/contrib/database/formula/test_replace_field_with_field_by_id.py create mode 100644 backend/tests/baserow/contrib/database/formula/test_update_field_names.py create mode 100644 backend/tests/baserow/contrib/database/migrations/test_remove_field_by_id_migration.py delete mode 100644 web-frontend/modules/database/formula/parser/replaceFieldByIdWithField.js delete mode 100644 web-frontend/modules/database/formula/parser/updateFieldNames.js delete mode 100644 web-frontend/test/unit/database/formula/replaceFieldByIdWithField.spec.js delete mode 100644 web-frontend/test/unit/database/formula/updateFieldNames.spec.js diff --git a/backend/src/baserow/contrib/database/api/formula/views.py b/backend/src/baserow/contrib/database/api/formula/views.py index 6c052e7fd..22cc9da44 100644 --- a/backend/src/baserow/contrib/database/api/formula/views.py +++ b/backend/src/baserow/contrib/database/api/formula/views.py @@ -80,6 +80,6 @@ class TypeFormulaView(APIView): field.formula = data["formula"] typed_table = type_table(field.table, overridden_field=field) # noinspection PyTypeChecker - typed_field: FormulaField = typed_table.get_typed_field_instance(field.id) + typed_field: FormulaField = typed_table.get_typed_field_instance(field.name) return Response(TypeFormulaResultSerializer(typed_field).data) diff --git a/backend/src/baserow/contrib/database/application_types.py b/backend/src/baserow/contrib/database/application_types.py index 923393efe..2a6f959d4 100644 --- a/backend/src/baserow/contrib/database/application_types.py +++ b/backend/src/baserow/contrib/database/application_types.py @@ -2,14 +2,11 @@ from django.core.management.color import no_style from django.db import connection from django.urls import path, include +from baserow.contrib.database.api.serializers import DatabaseSerializer from baserow.contrib.database.fields.registries import field_type_registry +from baserow.contrib.database.models import Database, Table from baserow.contrib.database.views.registries import view_type_registry from baserow.core.registries import ApplicationType -from baserow.contrib.database.api.serializers import DatabaseSerializer -from baserow.contrib.database.formula.types.typed_field_updater import ( - type_table_and_update_fields, -) -from baserow.contrib.database.models import Database, Table from baserow.core.trash.handler import TrashHandler @@ -145,17 +142,12 @@ class DatabaseApplicationType(ApplicationType): table["_object"], view, id_mapping, files_zip, storage ) - # Once all the fields have been deserialized and created we have to ensure - # all fields have been typed and their formulas have correctly been changed - # from containing field('..') to field_by_id(..). - typed_table = type_table_and_update_fields(table["_object"]) # We don't need to create all the fields individually because the schema # editor can handle the creation of the table schema in one go. with connection.schema_editor() as schema_editor: model = table["_object"].get_model( fields=table["_field_objects"], field_ids=[], - typed_table=typed_table, ) table["_model"] = model schema_editor.create_model(model) diff --git a/backend/src/baserow/contrib/database/fields/field_types.py b/backend/src/baserow/contrib/database/fields/field_types.py index 75fabe7de..bfa7558a9 100644 --- a/backend/src/baserow/contrib/database/fields/field_types.py +++ b/backend/src/baserow/contrib/database/fields/field_types.py @@ -37,9 +37,6 @@ from baserow.contrib.database.formula.exceptions import BaserowFormulaException from baserow.contrib.database.formula.expression_generator.generator import ( baserow_expression_to_django_expression, ) -from baserow.contrib.database.formula.parser.ast_mapper import ( - replace_field_refs_according_to_new_or_deleted_fields, -) from baserow.contrib.database.formula.types.formula_type import BaserowFormulaType from baserow.contrib.database.formula.types.formula_types import ( BaserowFormulaTextType, @@ -2178,28 +2175,6 @@ class FormulaFieldType(FieldType): else: return None - def export_serialized(self, field, include_allowed_fields=True): - serialized = super().export_serialized(field, include_allowed_fields) - if include_allowed_fields: - # Replace all field_by_id references back into their field('actual field - # name') format when serializing the formula to file. This enables us to - # easily re-import this formula into a table with new field ids as when - # typing that table we will automatically translate field('..') back into - # the field_by_id form but with the correct new field id's. If we did not - # do this step instead we would serialize formulas with field_by_id(N) - # where the id is a direct reference to a field in this particular table, - # meaning you could never import this field into a different table as it - # would be referencing an a field id in a different table. - - serialized[ - "formula" - ] = replace_field_refs_according_to_new_or_deleted_fields( - serialized["formula"], - {f.id: f.name for f in field.table.field_set.all()}, - {}, - ) - return serialized - def get_alter_column_prepare_old_value(self, connection, from_field, to_field): ( field_instance, @@ -2210,7 +2185,7 @@ class FormulaFieldType(FieldType): ) def add_related_fields_to_model( - self, typed_table, field, already_included_field_ids + self, typed_table, field, already_included_field_names ): # If we are building a model with some formula fields we need to # establish the types fields and whether they depend on any other @@ -2219,7 +2194,7 @@ class FormulaFieldType(FieldType): # Allow passing in typer=False to disable any type checking. if typed_table: return typed_table.get_all_depended_on_fields( - field, already_included_field_ids + field, already_included_field_names ) else: return [] diff --git a/backend/src/baserow/contrib/database/fields/handler.py b/backend/src/baserow/contrib/database/fields/handler.py index 2a61fde3b..d4158f168 100644 --- a/backend/src/baserow/contrib/database/fields/handler.py +++ b/backend/src/baserow/contrib/database/fields/handler.py @@ -7,11 +7,16 @@ from django.db import connection from django.db.utils import ProgrammingError, DataError from baserow.contrib.database.db.schema import lenient_schema_editor +from baserow.contrib.database.fields.constants import RESERVED_BASEROW_FIELD_NAMES +from baserow.contrib.database.formula.types.typed_field_updater import ( + type_table_and_update_fields_given_changed_field, + type_table_and_update_fields, + update_other_fields_referencing_this_fields_name, +) from baserow.contrib.database.table.models import Table from baserow.contrib.database.views.handler import ViewHandler from baserow.core.trash.handler import TrashHandler from baserow.core.utils import extract_allowed, set_allowed_attrs -from baserow.contrib.database.fields.constants import RESERVED_BASEROW_FIELD_NAMES from .exceptions import ( PrimaryFieldAlreadyExists, CannotDeletePrimaryField, @@ -27,10 +32,6 @@ from .exceptions import ( from .models import Field, SelectOption from .registries import field_type_registry, field_converter_registry from .signals import field_created, field_updated, field_deleted -from baserow.contrib.database.formula.types.typed_field_updater import ( - type_table_and_update_fields_given_changed_field, - type_table_and_update_fields_given_deleted_field, -) logger = logging.getLogger(__name__) @@ -84,6 +85,15 @@ def _validate_field_name( ) +def _merge_updated_fields( + updated_fields: List[Field], merged_sets: List[Field] +) -> List[Field]: + updated_fields_set = set(updated_fields) + merged_sets = set(merged_sets) + merged_sets.update(updated_fields_set) + return list(merged_sets) + + class FieldHandler: def get_field(self, field_id, field_model=None, base_queryset=None): """ @@ -299,6 +309,11 @@ class FieldHandler: field_values = field_type.prepare_values(field_values, user) before = field_type.before_update(old_field, field_values, user) + new_field_name = field_values.get("name", field.name) + fields_updated_due_to_name_change = ( + update_other_fields_referencing_this_fields_name(field, new_field_name) + ) + field = set_allowed_attrs(field_values, allowed_fields, field) field.save() typed_updated_table, field = type_table_and_update_fields_given_changed_field( @@ -406,15 +421,18 @@ class FieldHandler: ) typed_updated_table.update_values_for_all_updated_fields() + merged_updated_fields = _merge_updated_fields( + typed_updated_table.updated_fields, fields_updated_due_to_name_change + ) field_updated.send( self, field=field, - related_fields=typed_updated_table.updated_fields, + related_fields=merged_updated_fields, user=user, ) if return_updated_fields: - return field, typed_updated_table.updated_fields + return field, merged_updated_fields else: return field @@ -444,9 +462,7 @@ class FieldHandler: field = field.specific TrashHandler.trash(user, group, field.table.database, field) - typed_updated_table = type_table_and_update_fields_given_deleted_field( - field.table, deleted_field_id=field.id, deleted_field_name=field.name - ) + typed_updated_table = type_table_and_update_fields(field.table) field_deleted.send( self, field_id=field.id, diff --git a/backend/src/baserow/contrib/database/fields/models.py b/backend/src/baserow/contrib/database/fields/models.py index 36bb3415c..6254cfa82 100644 --- a/backend/src/baserow/contrib/database/fields/models.py +++ b/backend/src/baserow/contrib/database/fields/models.py @@ -277,6 +277,7 @@ class PhoneNumberField(Field): class FormulaField(Field): formula = models.TextField() + old_formula_with_field_by_id = models.TextField(null=True, blank=True) error = models.TextField(null=True, blank=True) formula_type = models.TextField( diff --git a/backend/src/baserow/contrib/database/fields/registries.py b/backend/src/baserow/contrib/database/fields/registries.py index b27459f7b..eea3d2b68 100644 --- a/backend/src/baserow/contrib/database/fields/registries.py +++ b/backend/src/baserow/contrib/database/fields/registries.py @@ -717,7 +717,7 @@ class FieldType( ) def add_related_fields_to_model( - self, typed_table, field, already_included_field_ids + self, typed_table, field, already_included_field_names ): """ Should return any fields related to this field which are not already present @@ -727,8 +727,8 @@ class FieldType( fields in the fields table. :param field: The specific instance of the field we want to know the related fields for. - :param already_included_field_ids: A set of already included field ids which - should not be included in the returned list. + :param already_included_field_names: A set of already included field names + whose fields should not be included in the returned list. :return: A list of field instances which relate to field but are not present in already_included_field_ids. """ diff --git a/backend/src/baserow/contrib/database/formula/ast/tree.py b/backend/src/baserow/contrib/database/formula/ast/tree.py index 75a186d43..6b16ec69d 100644 --- a/backend/src/baserow/contrib/database/formula/ast/tree.py +++ b/backend/src/baserow/contrib/database/formula/ast/tree.py @@ -190,38 +190,32 @@ class BaserowBooleanLiteral(BaserowExpression[A]): return str(self.literal) -class BaserowFieldByIdReference(BaserowExpression[A]): - """ - Represents a reference to a specific field with the referenced_field_id in the same - table. - """ - - def __init__(self, referenced_field_id: int, expression_type: A): - super().__init__(expression_type) - self.referenced_field_id = referenced_field_id - - def accept(self, visitor: "visitors.BaserowFormulaASTVisitor[A, T]") -> T: - return visitor.visit_field_by_id_reference(self) - - def __str__(self): - return f"field_by_id({self.referenced_field_id})" - - class BaserowFieldReference(BaserowExpression[A]): """ - Represents a reference to a field with the same name as the referenced_field_name - if it exists in the table. + Represents a reference to a field with the same name as the referenced_field_name. + If it is a valid reference to a real column then underlying_db_column will contain + the name of that column. Otherwise if a reference to an unknown or invalid field + underlying_db_column will be None. """ - def __init__(self, referenced_field_name: str, expression_type: A): + def __init__( + self, + referenced_field_name: str, + underlying_db_column: Optional[str], + expression_type: A, + ): super().__init__(expression_type) self.referenced_field_name = referenced_field_name + self.underlying_db_column = underlying_db_column def accept(self, visitor: "visitors.BaserowFormulaASTVisitor[A, T]") -> T: return visitor.visit_field_reference(self) + def is_reference_to_valid_field(self): + return self.underlying_db_column is not None + def __str__(self): - return f"field({self.referenced_field_name})" + return f"field({self.referenced_field_name}, {self.underlying_db_column})" class ArgCountSpecifier(abc.ABC): diff --git a/backend/src/baserow/contrib/database/formula/ast/visitors.py b/backend/src/baserow/contrib/database/formula/ast/visitors.py index e8daeec4b..2806ee77f 100644 --- a/backend/src/baserow/contrib/database/formula/ast/visitors.py +++ b/backend/src/baserow/contrib/database/formula/ast/visitors.py @@ -20,12 +20,6 @@ class BaserowFormulaASTVisitor(abc.ABC, Generic[Y, X]): def visit_int_literal(self, int_literal: "tree.BaserowIntegerLiteral[Y]") -> X: pass - @abc.abstractmethod - def visit_field_by_id_reference( - self, field_by_id_reference: "tree.BaserowFieldByIdReference[Y]" - ) -> X: - pass - @abc.abstractmethod def visit_field_reference( self, field_reference: "tree.BaserowFieldReference[Y]" diff --git a/backend/src/baserow/contrib/database/formula/expression_generator/generator.py b/backend/src/baserow/contrib/database/formula/expression_generator/generator.py index 6236dbcad..3de0f49eb 100644 --- a/backend/src/baserow/contrib/database/formula/expression_generator/generator.py +++ b/backend/src/baserow/contrib/database/formula/expression_generator/generator.py @@ -8,6 +8,7 @@ from django.db.models import ( BooleanField, fields, ExpressionWrapper, + Model, ) from django.db.models.functions import Cast @@ -16,7 +17,6 @@ from baserow.contrib.database.formula.ast.tree import ( BaserowStringLiteral, BaserowFunctionCall, BaserowIntegerLiteral, - BaserowFieldByIdReference, BaserowFieldReference, BaserowExpression, BaserowDecimalLiteral, @@ -25,18 +25,16 @@ from baserow.contrib.database.formula.ast.tree import ( from baserow.contrib.database.formula.ast.visitors import BaserowFormulaASTVisitor from baserow.contrib.database.formula.parser.exceptions import ( MaximumFormulaSizeError, - UnknownFieldByIdReference, ) from baserow.contrib.database.formula.types.formula_type import ( BaserowFormulaType, BaserowFormulaInvalidType, ) -from baserow.contrib.database.table import models def baserow_expression_to_django_expression( baserow_expression: BaserowExpression[BaserowFormulaType], - model_instance: Optional["models.GeneratedTableModel"], + model_instance: Optional[Model], ) -> Expression: """ Takes a BaserowExpression and converts it to a Django Expression which calculates @@ -92,36 +90,27 @@ class BaserowExpressionToDjangoExpressionGenerator( def __init__( self, - model_instance: Optional["models.GeneratedTableModel"], + model_instance: Optional[Model], ): self.model_instance = model_instance def visit_field_reference( self, field_reference: BaserowFieldReference[BaserowFormulaType] ): - # If a field() reference still exists it must not have been able to find a - # field with that name and replace it with a field_by_id. This means we cannot - # proceed as we do not know what field should be referenced here. - raise UnknownFieldReference(field_reference.referenced_field_name) + db_column = field_reference.underlying_db_column - def visit_field_by_id_reference( - self, field_by_id_reference: BaserowFieldByIdReference[BaserowFormulaType] - ): - field_id = field_by_id_reference.referenced_field_id - db_field_name = f"field_{field_id}" - - expression_type = field_by_id_reference.expression_type + expression_type = field_reference.expression_type model_field = _get_model_field_for_type(expression_type) if self.model_instance is None: - return ExpressionWrapper(F(db_field_name), output_field=model_field) - elif not hasattr(self.model_instance, db_field_name): - raise UnknownFieldByIdReference(field_id) + return ExpressionWrapper(F(db_column), output_field=model_field) + elif not hasattr(self.model_instance, db_column): + raise UnknownFieldReference(db_column) else: # We need to cast and be super explicit what type this raw value is so # postgres does not get angry and claim this is an unknown type. return Cast( Value( - getattr(self.model_instance, db_field_name), + getattr(self.model_instance, db_column), ), output_field=model_field, ) diff --git a/backend/src/baserow/contrib/database/formula/parser/ast_mapper.py b/backend/src/baserow/contrib/database/formula/parser/ast_mapper.py index 3aff4468c..0f7f236eb 100644 --- a/backend/src/baserow/contrib/database/formula/parser/ast_mapper.py +++ b/backend/src/baserow/contrib/database/formula/parser/ast_mapper.py @@ -1,14 +1,10 @@ from decimal import Decimal -from typing import Set - -from antlr4 import InputStream, CommonTokenStream -from antlr4.error.ErrorListener import ErrorListener +from typing import Dict from baserow.contrib.database.formula.ast.tree import ( BaserowStringLiteral, BaserowFunctionCall, BaserowIntegerLiteral, - BaserowFieldByIdReference, BaserowFieldReference, BaserowExpression, BaserowDecimalLiteral, @@ -17,54 +13,34 @@ from baserow.contrib.database.formula.ast.tree import ( from baserow.contrib.database.formula.parser.exceptions import ( InvalidNumberOfArguments, BaserowFormulaSyntaxError, - MaximumFormulaSizeError, UnknownOperator, - UnknownFieldByIdReference, + FieldByIdReferencesAreDeprecated, ) from baserow.contrib.database.formula.parser.generated.BaserowFormula import ( BaserowFormula, ) -from baserow.contrib.database.formula.parser.generated.BaserowFormulaLexer import ( - BaserowFormulaLexer, -) from baserow.contrib.database.formula.parser.generated.BaserowFormulaVisitor import ( BaserowFormulaVisitor, ) from baserow.contrib.database.formula.parser.parser import ( convert_string_literal_token_to_string, -) -from baserow.contrib.database.formula.parser.replace_field_by_id_with_field import ( - replace_field_by_id_with_field, -) -from baserow.contrib.database.formula.parser.replace_field_with_field_by_id import ( - replace_field_with_field_by_id, + get_parse_tree_for_formula, ) from baserow.contrib.database.formula.registries import formula_function_registry from baserow.contrib.database.formula.types.formula_type import UnTyped from baserow.core.exceptions import InstanceTypeDoesNotExist -class BaserowFormulaErrorListener(ErrorListener): - """ - A custom error listener as ANTLR's default error listen does not raise an - exception if a syntax error is found in a parse tree. - """ - - # noinspection PyPep8Naming - def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): - msg = msg.replace("<EOF>", "the end of the formula") - message = f"Invalid syntax at line {line}, col {column}: {msg}" - raise BaserowFormulaSyntaxError(message) - - def raw_formula_to_untyped_expression( - formula: str, valid_field_ids: Set[int] + formula: str, field_name_to_db_column: Dict[str, str] ) -> BaserowExpression[UnTyped]: """ Takes a raw user input string, syntax checks it to see if it matches the syntax of a Baserow Formula (raises a BaserowFormulaSyntaxError if not) and converts it into an untyped BaserowExpression. + :param field_name_to_db_column: The field names which are valid for the formula to + reference. :param formula: A raw user supplied string possibly in the format of a Baserow Formula. :return: An untyped BaserowExpression which represents the provided raw formula. @@ -72,49 +48,8 @@ def raw_formula_to_untyped_expression( of the Baserow Formula language. """ - lexer = BaserowFormulaLexer(InputStream(formula)) - stream = CommonTokenStream(lexer) - parser = BaserowFormula(stream) - parser.removeErrorListeners() - parser.addErrorListener(BaserowFormulaErrorListener()) - tree = parser.root() - return BaserowFormulaToBaserowASTMapper(valid_field_ids).visit(tree) - - -def replace_field_refs_according_to_new_or_deleted_fields( - formula: str, trash_ids_to_names, new_names_to_id -) -> str: - """ - Given a raw formula string lexs it into a token stream and goes through and replaces - all field_by_id references to a id in the provided trash_ids_to_names with a field - reference of the corresponding name. Does the opposite operation with - new_names_to_id and so will replace any field references with field_by_id according - to the names and ids in the dict. - - This method has to work off the tokens directly as once ANTLR parses a token stream - into a parse tree it will throw away all hidden channels, which for us are comments - and whitespace. Because we want to directly mutate the formula string and store the - result here we need to preserve whitespace and hence need to go off the raw tokens - which do still include the whitespace and comments. - - :param formula: A raw formula string to transform. - :param trash_ids_to_names: A dict of id to name to replace field_by_id(id) with - field(name) references. - :param new_names_to_id: A dict of name to id to replace field(name) with - field_by_id(id) references. - :return: A transformed formula string with field_by_id/field references substituted - according to the input dicts. Any whitespace or comments will be preserved and - still present in this returned formula string. - """ - try: - field_names_replaced_with_field_by_id = replace_field_with_field_by_id( - formula, new_names_to_id - ) - return replace_field_by_id_with_field( - field_names_replaced_with_field_by_id, trash_ids_to_names - ) - except RecursionError: - raise MaximumFormulaSizeError() + tree = get_parse_tree_for_formula(formula) + return BaserowFormulaToBaserowASTMapper(field_name_to_db_column).visit(tree) class BaserowFormulaToBaserowASTMapper(BaserowFormulaVisitor): @@ -127,8 +62,8 @@ class BaserowFormulaToBaserowASTMapper(BaserowFormulaVisitor): not in the registry. """ - def __init__(self, valid_field_ids: Set[int]): - self.valid_field_ids = valid_field_ids + def __init__(self, field_name_to_db_column: Dict[str, str]): + self.field_name_to_db_column = field_name_to_db_column def visitRoot(self, ctx: BaserowFormula.RootContext): return ctx.expr().accept(self) @@ -221,10 +156,19 @@ class BaserowFormulaToBaserowASTMapper(BaserowFormulaVisitor): field_name = convert_string_literal_token_to_string( reference.getText(), reference.SINGLEQ_STRING_LITERAL() ) - return BaserowFieldReference[UnTyped](field_name, None) + return BaserowFieldReference[UnTyped]( + field_name, self.field_name_to_db_column.get(field_name, None), None + ) def visitFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): - field_id = int(str(ctx.INTEGER_LITERAL())) - if field_id not in self.valid_field_ids: - raise UnknownFieldByIdReference(field_id) - return BaserowFieldByIdReference[UnTyped](field_id, None) + raise FieldByIdReferencesAreDeprecated() + + def visitLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + return ctx.expr().accept(self) + + def visitRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + return ctx.expr().accept(self) diff --git a/backend/src/baserow/contrib/database/formula/parser/exceptions.py b/backend/src/baserow/contrib/database/formula/parser/exceptions.py index 4469572aa..e6455377f 100644 --- a/backend/src/baserow/contrib/database/formula/parser/exceptions.py +++ b/backend/src/baserow/contrib/database/formula/parser/exceptions.py @@ -26,6 +26,14 @@ class UnknownFieldByIdReference(BaserowFormulaException): ) +class FieldByIdReferencesAreDeprecated(BaserowFormulaException): + def __init__(self): + super().__init__( + "It is no longer possible to reference a field by it's ID in the Baserow" + "formula language." + ) + + class UnknownOperator(BaserowFormulaException): def __init__(self, operatorText): super().__init__(f"it used the unknown operator {operatorText}") diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.interp b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.interp index 5b27890b7..442cad054 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.interp +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.interp @@ -85,9 +85,9 @@ null token symbolic names: null -WHITESPACE BLOCK_COMMENT LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -171,10 +171,11 @@ ErrorCharacter rule names: root expr +ws_or_comment func_name field_reference identifier atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 84, 74, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 40, 10, 3, 12, 3, 14, 3, 43, 11, 3, 5, 3, 45, 10, 3, 3, 3, 3, 3, 5, 3, 49, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 63, 10, 3, 12, 3, 14, 3, 66, 11, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 2, 3, 4, 7, 2, 4, 6, 8, 10, 2, 9, 3, 2, 6, 7, 4, 2, 15, 15, 74, 74, 4, 2, 62, 62, 68, 68, 4, 2, 42, 43, 53, 54, 4, 2, 38, 38, 40, 40, 3, 2, 26, 27, 3, 2, 28, 29, 2, 82, 2, 12, 3, 2, 2, 2, 4, 48, 3, 2, 2, 2, 6, 67, 3, 2, 2, 2, 8, 69, 3, 2, 2, 2, 10, 71, 3, 2, 2, 2, 12, 13, 5, 4, 3, 2, 13, 14, 7, 2, 2, 3, 14, 3, 3, 2, 2, 2, 15, 16, 8, 3, 1, 2, 16, 49, 7, 26, 2, 2, 17, 49, 7, 27, 2, 2, 18, 49, 7, 23, 2, 2, 19, 49, 7, 22, 2, 2, 20, 49, 9, 2, 2, 2, 21, 22, 7, 16, 2, 2, 22, 23, 5, 4, 3, 2, 23, 24, 7, 17, 2, 2, 24, 49, 3, 2, 2, 2, 25, 26, 7, 8, 2, 2, 26, 27, 7, 16, 2, 2, 27, 28, 5, 8, 5, 2, 28, 29, 7, 17, 2, 2, 29, 49, 3, 2, 2, 2, 30, 31, 7, 9, 2, 2, 31, 32, 7, 16, 2, 2, 32, 33, 7, 23, 2, 2, 33, 49, 7, 17, 2, 2, 34, 35, 5, 6, 4, 2, 35, 44, 7, 16, 2, 2, 36, 41, 5, 4, 3, 2, 37, 38, 7, 10, 2, 2, 38, 40, 5, 4, 3, 2, 39, 37, 3, 2, 2, 2, 40, 43, 3, 2, 2, 2, 41, 39, 3, 2, 2, 2, 41, 42, 3, 2, 2, 2, 42, 45, 3, 2, 2, 2, 43, 41, 3, 2, 2, 2, 44, 36, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 46, 3, 2, 2, 2, 46, 47, 7, 17, 2, 2, 47, 49, 3, 2, 2, 2, 48, 15, 3, 2, 2, 2, 48, 17, 3, 2, 2, 2, 48, 18, 3, 2, 2, 2, 48, 19, 3, 2, 2, 2, 48, 20, 3, 2, 2, 2, 48, 21, 3, 2, 2, 2, 48, 25, 3, 2, 2, 2, 48, 30, 3, 2, 2, 2, 48, 34, 3, 2, 2, 2, 49, 64, 3, 2, 2, 2, 50, 51, 12, 9, 2, 2, 51, 52, 9, 3, 2, 2, 52, 63, 5, 4, 3, 10, 53, 54, 12, 8, 2, 2, 54, 55, 9, 4, 2, 2, 55, 63, 5, 4, 3, 9, 56, 57, 12, 7, 2, 2, 57, 58, 9, 5, 2, 2, 58, 63, 5, 4, 3, 8, 59, 60, 12, 6, 2, 2, 60, 61, 9, 6, 2, 2, 61, 63, 5, 4, 3, 7, 62, 50, 3, 2, 2, 2, 62, 53, 3, 2, 2, 2, 62, 56, 3, 2, 2, 2, 62, 59, 3, 2, 2, 2, 63, 66, 3, 2, 2, 2, 64, 62, 3, 2, 2, 2, 64, 65, 3, 2, 2, 2, 65, 5, 3, 2, 2, 2, 66, 64, 3, 2, 2, 2, 67, 68, 5, 10, 6, 2, 68, 7, 3, 2, 2, 2, 69, 70, 9, 7, 2, 2, 70, 9, 3, 2, 2, 2, 71, 72, 9, 8, 2, 2, 72, 11, 3, 2, 2, 2, 7, 41, 44, 48, 62, 64] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 84, 83, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 45, 10, 3, 12, 3, 14, 3, 48, 11, 3, 5, 3, 50, 10, 3, 3, 3, 3, 3, 5, 3, 54, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 70, 10, 3, 12, 3, 14, 3, 73, 11, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 2, 3, 4, 8, 2, 4, 6, 8, 10, 12, 2, 10, 3, 2, 6, 7, 4, 2, 15, 15, 74, 74, 4, 2, 62, 62, 68, 68, 4, 2, 42, 43, 53, 54, 4, 2, 38, 38, 40, 40, 3, 2, 3, 5, 3, 2, 26, 27, 3, 2, 28, 29, 2, 92, 2, 14, 3, 2, 2, 2, 4, 53, 3, 2, 2, 2, 6, 74, 3, 2, 2, 2, 8, 76, 3, 2, 2, 2, 10, 78, 3, 2, 2, 2, 12, 80, 3, 2, 2, 2, 14, 15, 5, 4, 3, 2, 15, 16, 7, 2, 2, 3, 16, 3, 3, 2, 2, 2, 17, 18, 8, 3, 1, 2, 18, 54, 7, 26, 2, 2, 19, 54, 7, 27, 2, 2, 20, 54, 7, 23, 2, 2, 21, 54, 7, 22, 2, 2, 22, 54, 9, 2, 2, 2, 23, 24, 5, 6, 4, 2, 24, 25, 5, 4, 3, 12, 25, 54, 3, 2, 2, 2, 26, 27, 7, 16, 2, 2, 27, 28, 5, 4, 3, 2, 28, 29, 7, 17, 2, 2, 29, 54, 3, 2, 2, 2, 30, 31, 7, 8, 2, 2, 31, 32, 7, 16, 2, 2, 32, 33, 5, 10, 6, 2, 33, 34, 7, 17, 2, 2, 34, 54, 3, 2, 2, 2, 35, 36, 7, 9, 2, 2, 36, 37, 7, 16, 2, 2, 37, 38, 7, 23, 2, 2, 38, 54, 7, 17, 2, 2, 39, 40, 5, 8, 5, 2, 40, 49, 7, 16, 2, 2, 41, 46, 5, 4, 3, 2, 42, 43, 7, 10, 2, 2, 43, 45, 5, 4, 3, 2, 44, 42, 3, 2, 2, 2, 45, 48, 3, 2, 2, 2, 46, 44, 3, 2, 2, 2, 46, 47, 3, 2, 2, 2, 47, 50, 3, 2, 2, 2, 48, 46, 3, 2, 2, 2, 49, 41, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 7, 17, 2, 2, 52, 54, 3, 2, 2, 2, 53, 17, 3, 2, 2, 2, 53, 19, 3, 2, 2, 2, 53, 20, 3, 2, 2, 2, 53, 21, 3, 2, 2, 2, 53, 22, 3, 2, 2, 2, 53, 23, 3, 2, 2, 2, 53, 26, 3, 2, 2, 2, 53, 30, 3, 2, 2, 2, 53, 35, 3, 2, 2, 2, 53, 39, 3, 2, 2, 2, 54, 71, 3, 2, 2, 2, 55, 56, 12, 9, 2, 2, 56, 57, 9, 3, 2, 2, 57, 70, 5, 4, 3, 10, 58, 59, 12, 8, 2, 2, 59, 60, 9, 4, 2, 2, 60, 70, 5, 4, 3, 9, 61, 62, 12, 7, 2, 2, 62, 63, 9, 5, 2, 2, 63, 70, 5, 4, 3, 8, 64, 65, 12, 6, 2, 2, 65, 66, 9, 6, 2, 2, 66, 70, 5, 4, 3, 7, 67, 68, 12, 11, 2, 2, 68, 70, 5, 6, 4, 2, 69, 55, 3, 2, 2, 2, 69, 58, 3, 2, 2, 2, 69, 61, 3, 2, 2, 2, 69, 64, 3, 2, 2, 2, 69, 67, 3, 2, 2, 2, 70, 73, 3, 2, 2, 2, 71, 69, 3, 2, 2, 2, 71, 72, 3, 2, 2, 2, 72, 5, 3, 2, 2, 2, 73, 71, 3, 2, 2, 2, 74, 75, 9, 7, 2, 2, 75, 7, 3, 2, 2, 2, 76, 77, 5, 12, 7, 2, 77, 9, 3, 2, 2, 2, 78, 79, 9, 8, 2, 2, 79, 11, 3, 2, 2, 2, 80, 81, 9, 9, 2, 2, 81, 13, 3, 2, 2, 2, 7, 46, 49, 53, 69, 71] \ No newline at end of file diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.py b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.py index 4fdad19d0..2c5389604 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.py +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.py @@ -3,199 +3,346 @@ from antlr4 import * from io import StringIO import sys + if sys.version_info[1] > 5: - from typing import TextIO + from typing import TextIO else: - from typing.io import TextIO + from typing.io import TextIO def serializedATN(): with StringIO() as buf: buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3T") - buf.write("J\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\3\2\3\2\3\2") + buf.write("S\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\3\2") + buf.write("\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3") buf.write("\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3") - buf.write("\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\7\3(\n\3\f") - buf.write("\3\16\3+\13\3\5\3-\n\3\3\3\3\3\5\3\61\n\3\3\3\3\3\3\3") - buf.write("\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\7\3?\n\3\f\3\16\3") - buf.write("B\13\3\3\4\3\4\3\5\3\5\3\6\3\6\3\6\2\3\4\7\2\4\6\b\n\2") - buf.write("\t\3\2\6\7\4\2\17\17JJ\4\2>>DD\4\2*+\65\66\4\2&&((\3\2") - buf.write("\32\33\3\2\34\35\2R\2\f\3\2\2\2\4\60\3\2\2\2\6C\3\2\2") - buf.write("\2\bE\3\2\2\2\nG\3\2\2\2\f\r\5\4\3\2\r\16\7\2\2\3\16\3") - buf.write("\3\2\2\2\17\20\b\3\1\2\20\61\7\32\2\2\21\61\7\33\2\2\22") - buf.write("\61\7\27\2\2\23\61\7\26\2\2\24\61\t\2\2\2\25\26\7\20\2") - buf.write("\2\26\27\5\4\3\2\27\30\7\21\2\2\30\61\3\2\2\2\31\32\7") - buf.write("\b\2\2\32\33\7\20\2\2\33\34\5\b\5\2\34\35\7\21\2\2\35") - buf.write("\61\3\2\2\2\36\37\7\t\2\2\37 \7\20\2\2 !\7\27\2\2!\61") - buf.write("\7\21\2\2\"#\5\6\4\2#,\7\20\2\2$)\5\4\3\2%&\7\n\2\2&(") - buf.write("\5\4\3\2\'%\3\2\2\2(+\3\2\2\2)\'\3\2\2\2)*\3\2\2\2*-\3") - buf.write("\2\2\2+)\3\2\2\2,$\3\2\2\2,-\3\2\2\2-.\3\2\2\2./\7\21") - buf.write("\2\2/\61\3\2\2\2\60\17\3\2\2\2\60\21\3\2\2\2\60\22\3\2") - buf.write("\2\2\60\23\3\2\2\2\60\24\3\2\2\2\60\25\3\2\2\2\60\31\3") - buf.write("\2\2\2\60\36\3\2\2\2\60\"\3\2\2\2\61@\3\2\2\2\62\63\f") - buf.write("\t\2\2\63\64\t\3\2\2\64?\5\4\3\n\65\66\f\b\2\2\66\67\t") - buf.write("\4\2\2\67?\5\4\3\t89\f\7\2\29:\t\5\2\2:?\5\4\3\b;<\f\6") - buf.write("\2\2<=\t\6\2\2=?\5\4\3\7>\62\3\2\2\2>\65\3\2\2\2>8\3\2") - buf.write("\2\2>;\3\2\2\2?B\3\2\2\2@>\3\2\2\2@A\3\2\2\2A\5\3\2\2") - buf.write("\2B@\3\2\2\2CD\5\n\6\2D\7\3\2\2\2EF\t\7\2\2F\t\3\2\2\2") - buf.write("GH\t\b\2\2H\13\3\2\2\2\7),\60>@") + buf.write("\3\3\3\3\7\3-\n\3\f\3\16\3\60\13\3\5\3\62\n\3\3\3\3\3") + buf.write("\5\3\66\n\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3") + buf.write("\3\3\3\3\3\3\3\7\3F\n\3\f\3\16\3I\13\3\3\4\3\4\3\5\3\5") + buf.write("\3\6\3\6\3\7\3\7\3\7\2\3\4\b\2\4\6\b\n\f\2\n\3\2\6\7\4") + buf.write("\2\17\17JJ\4\2>>DD\4\2*+\65\66\4\2&&((\3\2\3\5\3\2\32") + buf.write("\33\3\2\34\35\2\\\2\16\3\2\2\2\4\65\3\2\2\2\6J\3\2\2\2") + buf.write("\bL\3\2\2\2\nN\3\2\2\2\fP\3\2\2\2\16\17\5\4\3\2\17\20") + buf.write("\7\2\2\3\20\3\3\2\2\2\21\22\b\3\1\2\22\66\7\32\2\2\23") + buf.write("\66\7\33\2\2\24\66\7\27\2\2\25\66\7\26\2\2\26\66\t\2\2") + buf.write("\2\27\30\5\6\4\2\30\31\5\4\3\f\31\66\3\2\2\2\32\33\7\20") + buf.write("\2\2\33\34\5\4\3\2\34\35\7\21\2\2\35\66\3\2\2\2\36\37") + buf.write('\7\b\2\2\37 \7\20\2\2 !\5\n\6\2!"\7\21\2\2"\66\3\2\2') + buf.write("\2#$\7\t\2\2$%\7\20\2\2%&\7\27\2\2&\66\7\21\2\2'(\5\b") + buf.write("\5\2(\61\7\20\2\2).\5\4\3\2*+\7\n\2\2+-\5\4\3\2,*\3\2") + buf.write("\2\2-\60\3\2\2\2.,\3\2\2\2./\3\2\2\2/\62\3\2\2\2\60.\3") + buf.write("\2\2\2\61)\3\2\2\2\61\62\3\2\2\2\62\63\3\2\2\2\63\64\7") + buf.write("\21\2\2\64\66\3\2\2\2\65\21\3\2\2\2\65\23\3\2\2\2\65\24") + buf.write("\3\2\2\2\65\25\3\2\2\2\65\26\3\2\2\2\65\27\3\2\2\2\65") + buf.write("\32\3\2\2\2\65\36\3\2\2\2\65#\3\2\2\2\65'\3\2\2\2\66") + buf.write("G\3\2\2\2\678\f\t\2\289\t\3\2\29F\5\4\3\n:;\f\b\2\2;<") + buf.write("\t\4\2\2<F\5\4\3\t=>\f\7\2\2>?\t\5\2\2?F\5\4\3\b@A\f\6") + buf.write("\2\2AB\t\6\2\2BF\5\4\3\7CD\f\13\2\2DF\5\6\4\2E\67\3\2") + buf.write("\2\2E:\3\2\2\2E=\3\2\2\2E@\3\2\2\2EC\3\2\2\2FI\3\2\2\2") + buf.write("GE\3\2\2\2GH\3\2\2\2H\5\3\2\2\2IG\3\2\2\2JK\t\7\2\2K\7") + buf.write("\3\2\2\2LM\5\f\7\2M\t\3\2\2\2NO\t\b\2\2O\13\3\2\2\2PQ") + buf.write("\t\t\2\2Q\r\3\2\2\2\7.\61\65EG") return buf.getvalue() -class BaserowFormula ( Parser ): +class BaserowFormula(Parser): grammarFileName = "BaserowFormula.g4" atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] sharedContextCache = PredictionContextCache() - literalNames = [ "<INVALID>", "<INVALID>", "<INVALID>", "<INVALID>", - "<INVALID>", "<INVALID>", "<INVALID>", "<INVALID>", - "','", "':'", "'::'", "'$'", "'$$'", "'*'", "'('", - "')'", "'['", "']'", "<INVALID>", "<INVALID>", "<INVALID>", - "<INVALID>", "<INVALID>", "'.'", "<INVALID>", "<INVALID>", - "<INVALID>", "<INVALID>", "'&'", "'&&'", "'&<'", "'@@'", - "'@>'", "'@'", "'!'", "'!!'", "'!='", "'^'", "'='", - "'=>'", "'>'", "'>='", "'>>'", "'#'", "'#='", "'#>'", - "'#>>'", "'##'", "'->'", "'->>'", "'-|-'", "'<'", "'<='", - "'<@'", "'<^'", "'<>'", "'<->'", "'<<'", "'<<='", "'<?>'", - "'-'", "'%'", "'|'", "'||'", "'||/'", "'|/'", "'+'", - "'?'", "'?&'", "'?#'", "'?-'", "'?|'", "'/'", "'~'", - "'~='", "'~>=~'", "'~>~'", "'~<=~'", "'~<~'", "'~*'", - "'~~'", "';'" ] + literalNames = [ + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "','", + "':'", + "'::'", + "'$'", + "'$$'", + "'*'", + "'('", + "')'", + "'['", + "']'", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "'.'", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "<INVALID>", + "'&'", + "'&&'", + "'&<'", + "'@@'", + "'@>'", + "'@'", + "'!'", + "'!!'", + "'!='", + "'^'", + "'='", + "'=>'", + "'>'", + "'>='", + "'>>'", + "'#'", + "'#='", + "'#>'", + "'#>>'", + "'##'", + "'->'", + "'->>'", + "'-|-'", + "'<'", + "'<='", + "'<@'", + "'<^'", + "'<>'", + "'<->'", + "'<<'", + "'<<='", + "'<?>'", + "'-'", + "'%'", + "'|'", + "'||'", + "'||/'", + "'|/'", + "'+'", + "'?'", + "'?&'", + "'?#'", + "'?-'", + "'?|'", + "'/'", + "'~'", + "'~='", + "'~>=~'", + "'~>~'", + "'~<=~'", + "'~<~'", + "'~*'", + "'~~'", + "';'", + ] - symbolicNames = [ "<INVALID>", "WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", - "TRUE", "FALSE", "FIELD", "FIELDBYID", "COMMA", "COLON", - "COLON_COLON", "DOLLAR", "DOLLAR_DOLLAR", "STAR", - "OPEN_PAREN", "CLOSE_PAREN", "OPEN_BRACKET", "CLOSE_BRACKET", - "BIT_STRING", "REGEX_STRING", "NUMERIC_LITERAL", "INTEGER_LITERAL", - "HEX_INTEGER_LITERAL", "DOT", "SINGLEQ_STRING_LITERAL", - "DOUBLEQ_STRING_LITERAL", "IDENTIFIER", "IDENTIFIER_UNICODE", - "AMP", "AMP_AMP", "AMP_LT", "AT_AT", "AT_GT", "AT_SIGN", - "BANG", "BANG_BANG", "BANG_EQUAL", "CARET", "EQUAL", - "EQUAL_GT", "GT", "GTE", "GT_GT", "HASH", "HASH_EQ", - "HASH_GT", "HASH_GT_GT", "HASH_HASH", "HYPHEN_GT", - "HYPHEN_GT_GT", "HYPHEN_PIPE_HYPHEN", "LT", "LTE", - "LT_AT", "LT_CARET", "LT_GT", "LT_HYPHEN_GT", "LT_LT", - "LT_LT_EQ", "LT_QMARK_GT", "MINUS", "PERCENT", "PIPE", - "PIPE_PIPE", "PIPE_PIPE_SLASH", "PIPE_SLASH", "PLUS", - "QMARK", "QMARK_AMP", "QMARK_HASH", "QMARK_HYPHEN", - "QMARK_PIPE", "SLASH", "TIL", "TIL_EQ", "TIL_GTE_TIL", - "TIL_GT_TIL", "TIL_LTE_TIL", "TIL_LT_TIL", "TIL_STAR", - "TIL_TIL", "SEMI", "ErrorCharacter" ] + symbolicNames = [ + "<INVALID>", + "BLOCK_COMMENT", + "LINE_COMMENT", + "WHITESPACE", + "TRUE", + "FALSE", + "FIELD", + "FIELDBYID", + "COMMA", + "COLON", + "COLON_COLON", + "DOLLAR", + "DOLLAR_DOLLAR", + "STAR", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACKET", + "CLOSE_BRACKET", + "BIT_STRING", + "REGEX_STRING", + "NUMERIC_LITERAL", + "INTEGER_LITERAL", + "HEX_INTEGER_LITERAL", + "DOT", + "SINGLEQ_STRING_LITERAL", + "DOUBLEQ_STRING_LITERAL", + "IDENTIFIER", + "IDENTIFIER_UNICODE", + "AMP", + "AMP_AMP", + "AMP_LT", + "AT_AT", + "AT_GT", + "AT_SIGN", + "BANG", + "BANG_BANG", + "BANG_EQUAL", + "CARET", + "EQUAL", + "EQUAL_GT", + "GT", + "GTE", + "GT_GT", + "HASH", + "HASH_EQ", + "HASH_GT", + "HASH_GT_GT", + "HASH_HASH", + "HYPHEN_GT", + "HYPHEN_GT_GT", + "HYPHEN_PIPE_HYPHEN", + "LT", + "LTE", + "LT_AT", + "LT_CARET", + "LT_GT", + "LT_HYPHEN_GT", + "LT_LT", + "LT_LT_EQ", + "LT_QMARK_GT", + "MINUS", + "PERCENT", + "PIPE", + "PIPE_PIPE", + "PIPE_PIPE_SLASH", + "PIPE_SLASH", + "PLUS", + "QMARK", + "QMARK_AMP", + "QMARK_HASH", + "QMARK_HYPHEN", + "QMARK_PIPE", + "SLASH", + "TIL", + "TIL_EQ", + "TIL_GTE_TIL", + "TIL_GT_TIL", + "TIL_LTE_TIL", + "TIL_LT_TIL", + "TIL_STAR", + "TIL_TIL", + "SEMI", + "ErrorCharacter", + ] RULE_root = 0 RULE_expr = 1 - RULE_func_name = 2 - RULE_field_reference = 3 - RULE_identifier = 4 + RULE_ws_or_comment = 2 + RULE_func_name = 3 + RULE_field_reference = 4 + RULE_identifier = 5 - ruleNames = [ "root", "expr", "func_name", "field_reference", "identifier" ] + ruleNames = [ + "root", + "expr", + "ws_or_comment", + "func_name", + "field_reference", + "identifier", + ] EOF = Token.EOF - WHITESPACE=1 - BLOCK_COMMENT=2 - LINE_COMMENT=3 - TRUE=4 - FALSE=5 - FIELD=6 - FIELDBYID=7 - COMMA=8 - COLON=9 - COLON_COLON=10 - DOLLAR=11 - DOLLAR_DOLLAR=12 - STAR=13 - OPEN_PAREN=14 - CLOSE_PAREN=15 - OPEN_BRACKET=16 - CLOSE_BRACKET=17 - BIT_STRING=18 - REGEX_STRING=19 - NUMERIC_LITERAL=20 - INTEGER_LITERAL=21 - HEX_INTEGER_LITERAL=22 - DOT=23 - SINGLEQ_STRING_LITERAL=24 - DOUBLEQ_STRING_LITERAL=25 - IDENTIFIER=26 - IDENTIFIER_UNICODE=27 - AMP=28 - AMP_AMP=29 - AMP_LT=30 - AT_AT=31 - AT_GT=32 - AT_SIGN=33 - BANG=34 - BANG_BANG=35 - BANG_EQUAL=36 - CARET=37 - EQUAL=38 - EQUAL_GT=39 - GT=40 - GTE=41 - GT_GT=42 - HASH=43 - HASH_EQ=44 - HASH_GT=45 - HASH_GT_GT=46 - HASH_HASH=47 - HYPHEN_GT=48 - HYPHEN_GT_GT=49 - HYPHEN_PIPE_HYPHEN=50 - LT=51 - LTE=52 - LT_AT=53 - LT_CARET=54 - LT_GT=55 - LT_HYPHEN_GT=56 - LT_LT=57 - LT_LT_EQ=58 - LT_QMARK_GT=59 - MINUS=60 - PERCENT=61 - PIPE=62 - PIPE_PIPE=63 - PIPE_PIPE_SLASH=64 - PIPE_SLASH=65 - PLUS=66 - QMARK=67 - QMARK_AMP=68 - QMARK_HASH=69 - QMARK_HYPHEN=70 - QMARK_PIPE=71 - SLASH=72 - TIL=73 - TIL_EQ=74 - TIL_GTE_TIL=75 - TIL_GT_TIL=76 - TIL_LTE_TIL=77 - TIL_LT_TIL=78 - TIL_STAR=79 - TIL_TIL=80 - SEMI=81 - ErrorCharacter=82 + BLOCK_COMMENT = 1 + LINE_COMMENT = 2 + WHITESPACE = 3 + TRUE = 4 + FALSE = 5 + FIELD = 6 + FIELDBYID = 7 + COMMA = 8 + COLON = 9 + COLON_COLON = 10 + DOLLAR = 11 + DOLLAR_DOLLAR = 12 + STAR = 13 + OPEN_PAREN = 14 + CLOSE_PAREN = 15 + OPEN_BRACKET = 16 + CLOSE_BRACKET = 17 + BIT_STRING = 18 + REGEX_STRING = 19 + NUMERIC_LITERAL = 20 + INTEGER_LITERAL = 21 + HEX_INTEGER_LITERAL = 22 + DOT = 23 + SINGLEQ_STRING_LITERAL = 24 + DOUBLEQ_STRING_LITERAL = 25 + IDENTIFIER = 26 + IDENTIFIER_UNICODE = 27 + AMP = 28 + AMP_AMP = 29 + AMP_LT = 30 + AT_AT = 31 + AT_GT = 32 + AT_SIGN = 33 + BANG = 34 + BANG_BANG = 35 + BANG_EQUAL = 36 + CARET = 37 + EQUAL = 38 + EQUAL_GT = 39 + GT = 40 + GTE = 41 + GT_GT = 42 + HASH = 43 + HASH_EQ = 44 + HASH_GT = 45 + HASH_GT_GT = 46 + HASH_HASH = 47 + HYPHEN_GT = 48 + HYPHEN_GT_GT = 49 + HYPHEN_PIPE_HYPHEN = 50 + LT = 51 + LTE = 52 + LT_AT = 53 + LT_CARET = 54 + LT_GT = 55 + LT_HYPHEN_GT = 56 + LT_LT = 57 + LT_LT_EQ = 58 + LT_QMARK_GT = 59 + MINUS = 60 + PERCENT = 61 + PIPE = 62 + PIPE_PIPE = 63 + PIPE_PIPE_SLASH = 64 + PIPE_SLASH = 65 + PLUS = 66 + QMARK = 67 + QMARK_AMP = 68 + QMARK_HASH = 69 + QMARK_HYPHEN = 70 + QMARK_PIPE = 71 + SLASH = 72 + TIL = 73 + TIL_EQ = 74 + TIL_GTE_TIL = 75 + TIL_GT_TIL = 76 + TIL_LTE_TIL = 77 + TIL_LT_TIL = 78 + TIL_STAR = 79 + TIL_TIL = 80 + SEMI = 81 + ErrorCharacter = 82 - def __init__(self, input:TokenStream, output:TextIO = sys.stdout): + def __init__(self, input: TokenStream, output: TextIO = sys.stdout): super().__init__(input, output) self.checkVersion("4.8") - self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self._interp = ParserATNSimulator( + self, self.atn, self.decisionsToDFA, self.sharedContextCache + ) self._predicates = None - - - class RootContext(ParserRuleContext): - - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def expr(self): - return self.getTypedRuleContext(BaserowFormula.ExprContext,0) - + return self.getTypedRuleContext(BaserowFormula.ExprContext, 0) def EOF(self): return self.getToken(BaserowFormula.EOF, 0) @@ -203,32 +350,29 @@ class BaserowFormula ( Parser ): def getRuleIndex(self): return BaserowFormula.RULE_root - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterRoot" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRoot"): listener.enterRoot(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitRoot" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRoot"): listener.exitRoot(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitRoot" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRoot"): return visitor.visitRoot(self) else: return visitor.visitChildren(self) - - - def root(self): localctx = BaserowFormula.RootContext(self, self._ctx, self.state) self.enterRule(localctx, 0, self.RULE_root) try: self.enterOuterAlt(localctx, 1) - self.state = 10 + self.state = 12 self.expr(0) - self.state = 11 + self.state = 13 self.match(BaserowFormula.EOF) except RecognitionException as re: localctx.exception = re @@ -238,313 +382,382 @@ class BaserowFormula ( Parser ): self.exitRule() return localctx - class ExprContext(ParserRuleContext): - - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser - def getRuleIndex(self): return BaserowFormula.RULE_expr - - def copyFrom(self, ctx:ParserRuleContext): + def copyFrom(self, ctx: ParserRuleContext): super().copyFrom(ctx) - class FieldReferenceContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def FIELD(self): return self.getToken(BaserowFormula.FIELD, 0) + def OPEN_PAREN(self): return self.getToken(BaserowFormula.OPEN_PAREN, 0) + def field_reference(self): - return self.getTypedRuleContext(BaserowFormula.Field_referenceContext,0) + return self.getTypedRuleContext(BaserowFormula.Field_referenceContext, 0) def CLOSE_PAREN(self): return self.getToken(BaserowFormula.CLOSE_PAREN, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterFieldReference" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldReference"): listener.enterFieldReference(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitFieldReference" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldReference"): listener.exitFieldReference(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitFieldReference" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldReference"): return visitor.visitFieldReference(self) else: return visitor.visitChildren(self) - class StringLiteralContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def SINGLEQ_STRING_LITERAL(self): return self.getToken(BaserowFormula.SINGLEQ_STRING_LITERAL, 0) + def DOUBLEQ_STRING_LITERAL(self): return self.getToken(BaserowFormula.DOUBLEQ_STRING_LITERAL, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterStringLiteral" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStringLiteral"): listener.enterStringLiteral(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitStringLiteral" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStringLiteral"): listener.exitStringLiteral(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitStringLiteral" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStringLiteral"): return visitor.visitStringLiteral(self) else: return visitor.visitChildren(self) - class BracketsContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def OPEN_PAREN(self): return self.getToken(BaserowFormula.OPEN_PAREN, 0) + def expr(self): - return self.getTypedRuleContext(BaserowFormula.ExprContext,0) + return self.getTypedRuleContext(BaserowFormula.ExprContext, 0) def CLOSE_PAREN(self): return self.getToken(BaserowFormula.CLOSE_PAREN, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBrackets" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBrackets"): listener.enterBrackets(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBrackets" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBrackets"): listener.exitBrackets(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBrackets" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBrackets"): return visitor.visitBrackets(self) else: return visitor.visitChildren(self) - class BooleanLiteralContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def TRUE(self): return self.getToken(BaserowFormula.TRUE, 0) + def FALSE(self): return self.getToken(BaserowFormula.FALSE, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBooleanLiteral" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBooleanLiteral"): listener.enterBooleanLiteral(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBooleanLiteral" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBooleanLiteral"): listener.exitBooleanLiteral(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBooleanLiteral" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBooleanLiteral"): return visitor.visitBooleanLiteral(self) else: return visitor.visitChildren(self) + class RightWhitespaceOrCommentsContext(ExprContext): + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext + super().__init__(parser) + self.copyFrom(ctx) + + def expr(self): + return self.getTypedRuleContext(BaserowFormula.ExprContext, 0) + + def ws_or_comment(self): + return self.getTypedRuleContext(BaserowFormula.Ws_or_commentContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRightWhitespaceOrComments"): + listener.enterRightWhitespaceOrComments(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRightWhitespaceOrComments"): + listener.exitRightWhitespaceOrComments(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRightWhitespaceOrComments"): + return visitor.visitRightWhitespaceOrComments(self) + else: + return visitor.visitChildren(self) class DecimalLiteralContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def NUMERIC_LITERAL(self): return self.getToken(BaserowFormula.NUMERIC_LITERAL, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterDecimalLiteral" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDecimalLiteral"): listener.enterDecimalLiteral(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitDecimalLiteral" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDecimalLiteral"): listener.exitDecimalLiteral(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitDecimalLiteral" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDecimalLiteral"): return visitor.visitDecimalLiteral(self) else: return visitor.visitChildren(self) + class LeftWhitespaceOrCommentsContext(ExprContext): + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext + super().__init__(parser) + self.copyFrom(ctx) + + def ws_or_comment(self): + return self.getTypedRuleContext(BaserowFormula.Ws_or_commentContext, 0) + + def expr(self): + return self.getTypedRuleContext(BaserowFormula.ExprContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLeftWhitespaceOrComments"): + listener.enterLeftWhitespaceOrComments(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLeftWhitespaceOrComments"): + listener.exitLeftWhitespaceOrComments(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLeftWhitespaceOrComments"): + return visitor.visitLeftWhitespaceOrComments(self) + else: + return visitor.visitChildren(self) class FunctionCallContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def func_name(self): - return self.getTypedRuleContext(BaserowFormula.Func_nameContext,0) + return self.getTypedRuleContext(BaserowFormula.Func_nameContext, 0) def OPEN_PAREN(self): return self.getToken(BaserowFormula.OPEN_PAREN, 0) + def CLOSE_PAREN(self): return self.getToken(BaserowFormula.CLOSE_PAREN, 0) - def expr(self, i:int=None): + + def expr(self, i: int = None): if i is None: return self.getTypedRuleContexts(BaserowFormula.ExprContext) else: - return self.getTypedRuleContext(BaserowFormula.ExprContext,i) + return self.getTypedRuleContext(BaserowFormula.ExprContext, i) - def COMMA(self, i:int=None): + def COMMA(self, i: int = None): if i is None: return self.getTokens(BaserowFormula.COMMA) else: return self.getToken(BaserowFormula.COMMA, i) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterFunctionCall" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFunctionCall"): listener.enterFunctionCall(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitFunctionCall" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFunctionCall"): listener.exitFunctionCall(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitFunctionCall" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFunctionCall"): return visitor.visitFunctionCall(self) else: return visitor.visitChildren(self) - class FieldByIdReferenceContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def FIELDBYID(self): return self.getToken(BaserowFormula.FIELDBYID, 0) + def OPEN_PAREN(self): return self.getToken(BaserowFormula.OPEN_PAREN, 0) + def INTEGER_LITERAL(self): return self.getToken(BaserowFormula.INTEGER_LITERAL, 0) + def CLOSE_PAREN(self): return self.getToken(BaserowFormula.CLOSE_PAREN, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterFieldByIdReference" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldByIdReference"): listener.enterFieldByIdReference(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitFieldByIdReference" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldByIdReference"): listener.exitFieldByIdReference(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitFieldByIdReference" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldByIdReference"): return visitor.visitFieldByIdReference(self) else: return visitor.visitChildren(self) - class IntegerLiteralContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) self.copyFrom(ctx) def INTEGER_LITERAL(self): return self.getToken(BaserowFormula.INTEGER_LITERAL, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIntegerLiteral" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIntegerLiteral"): listener.enterIntegerLiteral(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIntegerLiteral" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIntegerLiteral"): listener.exitIntegerLiteral(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIntegerLiteral" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIntegerLiteral"): return visitor.visitIntegerLiteral(self) else: return visitor.visitChildren(self) - class BinaryOpContext(ExprContext): - - def __init__(self, parser, ctx:ParserRuleContext): # actually a BaserowFormula.ExprContext + def __init__( + self, parser, ctx: ParserRuleContext + ): # actually a BaserowFormula.ExprContext super().__init__(parser) - self.op = None # Token + self.op = None # Token self.copyFrom(ctx) - def expr(self, i:int=None): + def expr(self, i: int = None): if i is None: return self.getTypedRuleContexts(BaserowFormula.ExprContext) else: - return self.getTypedRuleContext(BaserowFormula.ExprContext,i) + return self.getTypedRuleContext(BaserowFormula.ExprContext, i) def SLASH(self): return self.getToken(BaserowFormula.SLASH, 0) + def STAR(self): return self.getToken(BaserowFormula.STAR, 0) + def PLUS(self): return self.getToken(BaserowFormula.PLUS, 0) + def MINUS(self): return self.getToken(BaserowFormula.MINUS, 0) + def GT(self): return self.getToken(BaserowFormula.GT, 0) + def LT(self): return self.getToken(BaserowFormula.LT, 0) + def GTE(self): return self.getToken(BaserowFormula.GTE, 0) + def LTE(self): return self.getToken(BaserowFormula.LTE, 0) + def EQUAL(self): return self.getToken(BaserowFormula.EQUAL, 0) + def BANG_EQUAL(self): return self.getToken(BaserowFormula.BANG_EQUAL, 0) - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterBinaryOp" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBinaryOp"): listener.enterBinaryOp(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitBinaryOp" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBinaryOp"): listener.exitBinaryOp(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitBinaryOp" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBinaryOp"): return visitor.visitBinaryOp(self) else: return visitor.visitChildren(self) - - - def expr(self, _p:int=0): + def expr(self, _p: int = 0): _parentctx = self._ctx _parentState = self.state localctx = BaserowFormula.ExprContext(self, self._ctx, _parentState) _prevctx = localctx _startState = 2 self.enterRecursionRule(localctx, 2, self.RULE_expr, _p) - self._la = 0 # Token type + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 46 + self.state = 51 self._errHandler.sync(self) token = self._input.LA(1) if token in [BaserowFormula.SINGLEQ_STRING_LITERAL]: @@ -552,205 +765,310 @@ class BaserowFormula ( Parser ): self._ctx = localctx _prevctx = localctx - self.state = 14 + self.state = 16 self.match(BaserowFormula.SINGLEQ_STRING_LITERAL) pass elif token in [BaserowFormula.DOUBLEQ_STRING_LITERAL]: localctx = BaserowFormula.StringLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 15 + self.state = 17 self.match(BaserowFormula.DOUBLEQ_STRING_LITERAL) pass elif token in [BaserowFormula.INTEGER_LITERAL]: localctx = BaserowFormula.IntegerLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 16 + self.state = 18 self.match(BaserowFormula.INTEGER_LITERAL) pass elif token in [BaserowFormula.NUMERIC_LITERAL]: localctx = BaserowFormula.DecimalLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 17 + self.state = 19 self.match(BaserowFormula.NUMERIC_LITERAL) pass elif token in [BaserowFormula.TRUE, BaserowFormula.FALSE]: localctx = BaserowFormula.BooleanLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 18 + self.state = 20 _la = self._input.LA(1) - if not(_la==BaserowFormula.TRUE or _la==BaserowFormula.FALSE): + if not (_la == BaserowFormula.TRUE or _la == BaserowFormula.FALSE): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() pass + elif token in [ + BaserowFormula.BLOCK_COMMENT, + BaserowFormula.LINE_COMMENT, + BaserowFormula.WHITESPACE, + ]: + localctx = BaserowFormula.LeftWhitespaceOrCommentsContext( + self, localctx + ) + self._ctx = localctx + _prevctx = localctx + self.state = 21 + self.ws_or_comment() + self.state = 22 + self.expr(10) + pass elif token in [BaserowFormula.OPEN_PAREN]: localctx = BaserowFormula.BracketsContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 19 + self.state = 24 self.match(BaserowFormula.OPEN_PAREN) - self.state = 20 + self.state = 25 self.expr(0) - self.state = 21 + self.state = 26 self.match(BaserowFormula.CLOSE_PAREN) pass elif token in [BaserowFormula.FIELD]: localctx = BaserowFormula.FieldReferenceContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 23 + self.state = 28 self.match(BaserowFormula.FIELD) - self.state = 24 + self.state = 29 self.match(BaserowFormula.OPEN_PAREN) - self.state = 25 + self.state = 30 self.field_reference() - self.state = 26 + self.state = 31 self.match(BaserowFormula.CLOSE_PAREN) pass elif token in [BaserowFormula.FIELDBYID]: localctx = BaserowFormula.FieldByIdReferenceContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 28 + self.state = 33 self.match(BaserowFormula.FIELDBYID) - self.state = 29 + self.state = 34 self.match(BaserowFormula.OPEN_PAREN) - self.state = 30 + self.state = 35 self.match(BaserowFormula.INTEGER_LITERAL) - self.state = 31 + self.state = 36 self.match(BaserowFormula.CLOSE_PAREN) pass - elif token in [BaserowFormula.IDENTIFIER, BaserowFormula.IDENTIFIER_UNICODE]: + elif token in [ + BaserowFormula.IDENTIFIER, + BaserowFormula.IDENTIFIER_UNICODE, + ]: localctx = BaserowFormula.FunctionCallContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 32 + self.state = 37 self.func_name() - self.state = 33 + self.state = 38 self.match(BaserowFormula.OPEN_PAREN) - self.state = 42 + self.state = 47 self._errHandler.sync(self) _la = self._input.LA(1) - if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << BaserowFormula.TRUE) | (1 << BaserowFormula.FALSE) | (1 << BaserowFormula.FIELD) | (1 << BaserowFormula.FIELDBYID) | (1 << BaserowFormula.OPEN_PAREN) | (1 << BaserowFormula.NUMERIC_LITERAL) | (1 << BaserowFormula.INTEGER_LITERAL) | (1 << BaserowFormula.SINGLEQ_STRING_LITERAL) | (1 << BaserowFormula.DOUBLEQ_STRING_LITERAL) | (1 << BaserowFormula.IDENTIFIER) | (1 << BaserowFormula.IDENTIFIER_UNICODE))) != 0): - self.state = 34 - self.expr(0) + if ((_la) & ~0x3F) == 0 and ( + (1 << _la) + & ( + (1 << BaserowFormula.BLOCK_COMMENT) + | (1 << BaserowFormula.LINE_COMMENT) + | (1 << BaserowFormula.WHITESPACE) + | (1 << BaserowFormula.TRUE) + | (1 << BaserowFormula.FALSE) + | (1 << BaserowFormula.FIELD) + | (1 << BaserowFormula.FIELDBYID) + | (1 << BaserowFormula.OPEN_PAREN) + | (1 << BaserowFormula.NUMERIC_LITERAL) + | (1 << BaserowFormula.INTEGER_LITERAL) + | (1 << BaserowFormula.SINGLEQ_STRING_LITERAL) + | (1 << BaserowFormula.DOUBLEQ_STRING_LITERAL) + | (1 << BaserowFormula.IDENTIFIER) + | (1 << BaserowFormula.IDENTIFIER_UNICODE) + ) + ) != 0: self.state = 39 + self.expr(0) + self.state = 44 self._errHandler.sync(self) _la = self._input.LA(1) - while _la==BaserowFormula.COMMA: - self.state = 35 + while _la == BaserowFormula.COMMA: + self.state = 40 self.match(BaserowFormula.COMMA) - self.state = 36 - self.expr(0) self.state = 41 + self.expr(0) + self.state = 46 self._errHandler.sync(self) _la = self._input.LA(1) - - - self.state = 44 + self.state = 49 self.match(BaserowFormula.CLOSE_PAREN) pass else: raise NoViableAltException(self) self._ctx.stop = self._input.LT(-1) - self.state = 62 + self.state = 69 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,4,self._ctx) - while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: - if _alt==1: + _alt = self._interp.adaptivePredict(self._input, 4, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 60 + self.state = 67 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,3,self._ctx) + la_ = self._interp.adaptivePredict(self._input, 3, self._ctx) if la_ == 1: - localctx = BaserowFormula.BinaryOpContext(self, BaserowFormula.ExprContext(self, _parentctx, _parentState)) - self.pushNewRecursionContext(localctx, _startState, self.RULE_expr) - self.state = 48 + localctx = BaserowFormula.BinaryOpContext( + self, + BaserowFormula.ExprContext(self, _parentctx, _parentState), + ) + self.pushNewRecursionContext( + localctx, _startState, self.RULE_expr + ) + self.state = 53 if not self.precpred(self._ctx, 7): from antlr4.error.Errors import FailedPredicateException - raise FailedPredicateException(self, "self.precpred(self._ctx, 7)") - self.state = 49 + + raise FailedPredicateException( + self, "self.precpred(self._ctx, 7)" + ) + self.state = 54 localctx.op = self._input.LT(1) _la = self._input.LA(1) - if not(_la==BaserowFormula.STAR or _la==BaserowFormula.SLASH): + if not ( + _la == BaserowFormula.STAR or _la == BaserowFormula.SLASH + ): localctx.op = self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 50 + self.state = 55 self.expr(8) pass elif la_ == 2: - localctx = BaserowFormula.BinaryOpContext(self, BaserowFormula.ExprContext(self, _parentctx, _parentState)) - self.pushNewRecursionContext(localctx, _startState, self.RULE_expr) - self.state = 51 + localctx = BaserowFormula.BinaryOpContext( + self, + BaserowFormula.ExprContext(self, _parentctx, _parentState), + ) + self.pushNewRecursionContext( + localctx, _startState, self.RULE_expr + ) + self.state = 56 if not self.precpred(self._ctx, 6): from antlr4.error.Errors import FailedPredicateException - raise FailedPredicateException(self, "self.precpred(self._ctx, 6)") - self.state = 52 + + raise FailedPredicateException( + self, "self.precpred(self._ctx, 6)" + ) + self.state = 57 localctx.op = self._input.LT(1) _la = self._input.LA(1) - if not(_la==BaserowFormula.MINUS or _la==BaserowFormula.PLUS): + if not ( + _la == BaserowFormula.MINUS or _la == BaserowFormula.PLUS + ): localctx.op = self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 53 + self.state = 58 self.expr(7) pass elif la_ == 3: - localctx = BaserowFormula.BinaryOpContext(self, BaserowFormula.ExprContext(self, _parentctx, _parentState)) - self.pushNewRecursionContext(localctx, _startState, self.RULE_expr) - self.state = 54 + localctx = BaserowFormula.BinaryOpContext( + self, + BaserowFormula.ExprContext(self, _parentctx, _parentState), + ) + self.pushNewRecursionContext( + localctx, _startState, self.RULE_expr + ) + self.state = 59 if not self.precpred(self._ctx, 5): from antlr4.error.Errors import FailedPredicateException - raise FailedPredicateException(self, "self.precpred(self._ctx, 5)") - self.state = 55 + + raise FailedPredicateException( + self, "self.precpred(self._ctx, 5)" + ) + self.state = 60 localctx.op = self._input.LT(1) _la = self._input.LA(1) - if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << BaserowFormula.GT) | (1 << BaserowFormula.GTE) | (1 << BaserowFormula.LT) | (1 << BaserowFormula.LTE))) != 0)): + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << BaserowFormula.GT) + | (1 << BaserowFormula.GTE) + | (1 << BaserowFormula.LT) + | (1 << BaserowFormula.LTE) + ) + ) + != 0 + ) + ): localctx.op = self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 56 + self.state = 61 self.expr(6) pass elif la_ == 4: - localctx = BaserowFormula.BinaryOpContext(self, BaserowFormula.ExprContext(self, _parentctx, _parentState)) - self.pushNewRecursionContext(localctx, _startState, self.RULE_expr) - self.state = 57 + localctx = BaserowFormula.BinaryOpContext( + self, + BaserowFormula.ExprContext(self, _parentctx, _parentState), + ) + self.pushNewRecursionContext( + localctx, _startState, self.RULE_expr + ) + self.state = 62 if not self.precpred(self._ctx, 4): from antlr4.error.Errors import FailedPredicateException - raise FailedPredicateException(self, "self.precpred(self._ctx, 4)") - self.state = 58 + + raise FailedPredicateException( + self, "self.precpred(self._ctx, 4)" + ) + self.state = 63 localctx.op = self._input.LT(1) _la = self._input.LA(1) - if not(_la==BaserowFormula.BANG_EQUAL or _la==BaserowFormula.EQUAL): + if not ( + _la == BaserowFormula.BANG_EQUAL + or _la == BaserowFormula.EQUAL + ): localctx.op = self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 59 + self.state = 64 self.expr(5) pass - - self.state = 64 + elif la_ == 5: + localctx = BaserowFormula.RightWhitespaceOrCommentsContext( + self, + BaserowFormula.ExprContext(self, _parentctx, _parentState), + ) + self.pushNewRecursionContext( + localctx, _startState, self.RULE_expr + ) + self.state = 65 + if not self.precpred(self._ctx, 9): + from antlr4.error.Errors import FailedPredicateException + + raise FailedPredicateException( + self, "self.precpred(self._ctx, 9)" + ) + self.state = 66 + self.ws_or_comment() + pass + + self.state = 71 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,4,self._ctx) + _alt = self._interp.adaptivePredict(self._input, 4, self._ctx) except RecognitionException as re: localctx.exception = re @@ -760,44 +1078,108 @@ class BaserowFormula ( Parser ): self.unrollRecursionContexts(_parentctx) return localctx + class Ws_or_commentContext(ParserRuleContext): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): + super().__init__(parent, invokingState) + self.parser = parser + + def BLOCK_COMMENT(self): + return self.getToken(BaserowFormula.BLOCK_COMMENT, 0) + + def LINE_COMMENT(self): + return self.getToken(BaserowFormula.LINE_COMMENT, 0) + + def WHITESPACE(self): + return self.getToken(BaserowFormula.WHITESPACE, 0) + + def getRuleIndex(self): + return BaserowFormula.RULE_ws_or_comment + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterWs_or_comment"): + listener.enterWs_or_comment(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitWs_or_comment"): + listener.exitWs_or_comment(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitWs_or_comment"): + return visitor.visitWs_or_comment(self) + else: + return visitor.visitChildren(self) + + def ws_or_comment(self): + + localctx = BaserowFormula.Ws_or_commentContext(self, self._ctx, self.state) + self.enterRule(localctx, 4, self.RULE_ws_or_comment) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 72 + _la = self._input.LA(1) + if not ( + ( + ((_la) & ~0x3F) == 0 + and ( + (1 << _la) + & ( + (1 << BaserowFormula.BLOCK_COMMENT) + | (1 << BaserowFormula.LINE_COMMENT) + | (1 << BaserowFormula.WHITESPACE) + ) + ) + != 0 + ) + ): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx class Func_nameContext(ParserRuleContext): - - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser def identifier(self): - return self.getTypedRuleContext(BaserowFormula.IdentifierContext,0) - + return self.getTypedRuleContext(BaserowFormula.IdentifierContext, 0) def getRuleIndex(self): return BaserowFormula.RULE_func_name - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterFunc_name" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFunc_name"): listener.enterFunc_name(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitFunc_name" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFunc_name"): listener.exitFunc_name(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitFunc_name" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFunc_name"): return visitor.visitFunc_name(self) else: return visitor.visitChildren(self) - - - def func_name(self): localctx = BaserowFormula.Func_nameContext(self, self._ctx, self.state) - self.enterRule(localctx, 4, self.RULE_func_name) + self.enterRule(localctx, 6, self.RULE_func_name) try: self.enterOuterAlt(localctx, 1) - self.state = 65 + self.state = 74 self.identifier() except RecognitionException as re: localctx.exception = re @@ -807,10 +1189,10 @@ class BaserowFormula ( Parser ): self.exitRule() return localctx - class Field_referenceContext(ParserRuleContext): - - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -823,33 +1205,33 @@ class BaserowFormula ( Parser ): def getRuleIndex(self): return BaserowFormula.RULE_field_reference - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterField_reference" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterField_reference"): listener.enterField_reference(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitField_reference" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitField_reference"): listener.exitField_reference(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitField_reference" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitField_reference"): return visitor.visitField_reference(self) else: return visitor.visitChildren(self) - - - def field_reference(self): localctx = BaserowFormula.Field_referenceContext(self, self._ctx, self.state) - self.enterRule(localctx, 6, self.RULE_field_reference) - self._la = 0 # Token type + self.enterRule(localctx, 8, self.RULE_field_reference) + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 67 + self.state = 76 _la = self._input.LA(1) - if not(_la==BaserowFormula.SINGLEQ_STRING_LITERAL or _la==BaserowFormula.DOUBLEQ_STRING_LITERAL): + if not ( + _la == BaserowFormula.SINGLEQ_STRING_LITERAL + or _la == BaserowFormula.DOUBLEQ_STRING_LITERAL + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -862,10 +1244,10 @@ class BaserowFormula ( Parser ): self.exitRule() return localctx - class IdentifierContext(ParserRuleContext): - - def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + def __init__( + self, parser, parent: ParserRuleContext = None, invokingState: int = -1 + ): super().__init__(parent, invokingState) self.parser = parser @@ -878,33 +1260,33 @@ class BaserowFormula ( Parser ): def getRuleIndex(self): return BaserowFormula.RULE_identifier - def enterRule(self, listener:ParseTreeListener): - if hasattr( listener, "enterIdentifier" ): + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIdentifier"): listener.enterIdentifier(self) - def exitRule(self, listener:ParseTreeListener): - if hasattr( listener, "exitIdentifier" ): + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIdentifier"): listener.exitIdentifier(self) - def accept(self, visitor:ParseTreeVisitor): - if hasattr( visitor, "visitIdentifier" ): + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIdentifier"): return visitor.visitIdentifier(self) else: return visitor.visitChildren(self) - - - def identifier(self): localctx = BaserowFormula.IdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 8, self.RULE_identifier) - self._la = 0 # Token type + self.enterRule(localctx, 10, self.RULE_identifier) + self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 69 + self.state = 78 _la = self._input.LA(1) - if not(_la==BaserowFormula.IDENTIFIER or _la==BaserowFormula.IDENTIFIER_UNICODE): + if not ( + _la == BaserowFormula.IDENTIFIER + or _la == BaserowFormula.IDENTIFIER_UNICODE + ): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) @@ -917,9 +1299,7 @@ class BaserowFormula ( Parser ): self.exitRule() return localctx - - - def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int): + def sempred(self, localctx: RuleContext, ruleIndex: int, predIndex: int): if self._predicates == None: self._predicates = dict() self._predicates[1] = self.expr_sempred @@ -929,23 +1309,18 @@ class BaserowFormula ( Parser ): else: return pred(localctx, predIndex) - def expr_sempred(self, localctx:ExprContext, predIndex:int): - if predIndex == 0: - return self.precpred(self._ctx, 7) - - - if predIndex == 1: - return self.precpred(self._ctx, 6) - - - if predIndex == 2: - return self.precpred(self._ctx, 5) - - - if predIndex == 3: - return self.precpred(self._ctx, 4) - + def expr_sempred(self, localctx: ExprContext, predIndex: int): + if predIndex == 0: + return self.precpred(self._ctx, 7) + if predIndex == 1: + return self.precpred(self._ctx, 6) + if predIndex == 2: + return self.precpred(self._ctx, 5) + if predIndex == 3: + return self.precpred(self._ctx, 4) + if predIndex == 4: + return self.precpred(self._ctx, 9) diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.tokens b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.tokens index 805982389..1b6673037 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.tokens +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormula.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.interp b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.interp index 6844f1e6a..b2b0f34b7 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.interp +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.interp @@ -85,9 +85,9 @@ null token symbolic names: null -WHITESPACE BLOCK_COMMENT LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -169,9 +169,6 @@ SEMI ErrorCharacter rule names: -WHITESPACE -BLOCK_COMMENT -LINE_COMMENT A B C @@ -204,6 +201,9 @@ DEC_DIGIT DQUOTA_STRING SQUOTA_STRING BQUOTA_STRING +BLOCK_COMMENT +LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -292,4 +292,4 @@ mode names: DEFAULT_MODE atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 84, 650, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 3, 2, 6, 2, 233, 10, 2, 13, 2, 14, 2, 234, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 243, 10, 3, 12, 3, 14, 3, 246, 11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 257, 10, 4, 12, 4, 14, 4, 260, 11, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 7, 34, 328, 10, 34, 12, 34, 14, 34, 331, 11, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 7, 35, 339, 10, 35, 12, 35, 14, 35, 342, 11, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 7, 36, 352, 10, 36, 12, 36, 14, 36, 355, 11, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 7, 51, 413, 10, 51, 12, 51, 14, 51, 416, 11, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 5, 53, 424, 10, 53, 3, 53, 6, 53, 427, 10, 53, 13, 53, 14, 53, 428, 3, 53, 3, 53, 6, 53, 433, 10, 53, 13, 53, 14, 53, 434, 3, 53, 3, 53, 7, 53, 439, 10, 53, 12, 53, 14, 53, 442, 11, 53, 3, 53, 6, 53, 445, 10, 53, 13, 53, 14, 53, 446, 5, 53, 449, 10, 53, 3, 54, 5, 54, 452, 10, 54, 3, 54, 6, 54, 455, 10, 54, 13, 54, 14, 54, 456, 3, 54, 3, 54, 6, 54, 461, 10, 54, 13, 54, 14, 54, 462, 5, 54, 465, 10, 54, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 7, 59, 478, 10, 59, 12, 59, 14, 59, 481, 11, 59, 3, 60, 3, 60, 7, 60, 485, 10, 60, 12, 60, 14, 60, 488, 11, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 82, 3, 83, 3, 83, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 96, 3, 97, 3, 97, 3, 97, 3, 97, 3, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 101, 3, 102, 3, 102, 3, 102, 3, 103, 3, 103, 3, 103, 3, 104, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 107, 3, 108, 3, 108, 3, 108, 3, 108, 3, 108, 3, 109, 3, 109, 3, 109, 3, 109, 3, 110, 3, 110, 3, 110, 3, 110, 3, 110, 3, 111, 3, 111, 3, 111, 3, 111, 3, 112, 3, 112, 3, 112, 3, 113, 3, 113, 3, 113, 3, 114, 3, 114, 3, 115, 3, 115, 4, 244, 258, 2, 116, 3, 3, 5, 4, 7, 5, 9, 2, 11, 2, 13, 2, 15, 2, 17, 2, 19, 2, 21, 2, 23, 2, 25, 2, 27, 2, 29, 2, 31, 2, 33, 2, 35, 2, 37, 2, 39, 2, 41, 2, 43, 2, 45, 2, 47, 2, 49, 2, 51, 2, 53, 2, 55, 2, 57, 2, 59, 2, 61, 2, 63, 2, 65, 2, 67, 2, 69, 2, 71, 2, 73, 6, 75, 7, 77, 8, 79, 9, 81, 10, 83, 11, 85, 12, 87, 13, 89, 14, 91, 15, 93, 16, 95, 17, 97, 18, 99, 19, 101, 20, 103, 21, 105, 22, 107, 23, 109, 24, 111, 25, 113, 26, 115, 27, 117, 28, 119, 29, 121, 30, 123, 31, 125, 32, 127, 33, 129, 34, 131, 35, 133, 36, 135, 37, 137, 38, 139, 39, 141, 40, 143, 41, 145, 42, 147, 43, 149, 44, 151, 45, 153, 46, 155, 47, 157, 48, 159, 49, 161, 50, 163, 51, 165, 52, 167, 53, 169, 54, 171, 55, 173, 56, 175, 57, 177, 58, 179, 59, 181, 60, 183, 61, 185, 62, 187, 63, 189, 64, 191, 65, 193, 66, 195, 67, 197, 68, 199, 69, 201, 70, 203, 71, 205, 72, 207, 73, 209, 74, 211, 75, 213, 76, 215, 77, 217, 78, 219, 79, 221, 80, 223, 81, 225, 82, 227, 83, 229, 84, 3, 2, 38, 5, 2, 11, 12, 15, 15, 34, 34, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 4, 2, 50, 59, 67, 72, 3, 2, 50, 59, 4, 2, 36, 36, 94, 94, 4, 2, 41, 41, 94, 94, 4, 2, 94, 94, 98, 98, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 6, 2, 67, 92, 97, 97, 99, 124, 163, 1, 7, 2, 50, 59, 67, 92, 97, 97, 99, 124, 163, 1, 2, 640, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 157, 3, 2, 2, 2, 2, 159, 3, 2, 2, 2, 2, 161, 3, 2, 2, 2, 2, 163, 3, 2, 2, 2, 2, 165, 3, 2, 2, 2, 2, 167, 3, 2, 2, 2, 2, 169, 3, 2, 2, 2, 2, 171, 3, 2, 2, 2, 2, 173, 3, 2, 2, 2, 2, 175, 3, 2, 2, 2, 2, 177, 3, 2, 2, 2, 2, 179, 3, 2, 2, 2, 2, 181, 3, 2, 2, 2, 2, 183, 3, 2, 2, 2, 2, 185, 3, 2, 2, 2, 2, 187, 3, 2, 2, 2, 2, 189, 3, 2, 2, 2, 2, 191, 3, 2, 2, 2, 2, 193, 3, 2, 2, 2, 2, 195, 3, 2, 2, 2, 2, 197, 3, 2, 2, 2, 2, 199, 3, 2, 2, 2, 2, 201, 3, 2, 2, 2, 2, 203, 3, 2, 2, 2, 2, 205, 3, 2, 2, 2, 2, 207, 3, 2, 2, 2, 2, 209, 3, 2, 2, 2, 2, 211, 3, 2, 2, 2, 2, 213, 3, 2, 2, 2, 2, 215, 3, 2, 2, 2, 2, 217, 3, 2, 2, 2, 2, 219, 3, 2, 2, 2, 2, 221, 3, 2, 2, 2, 2, 223, 3, 2, 2, 2, 2, 225, 3, 2, 2, 2, 2, 227, 3, 2, 2, 2, 2, 229, 3, 2, 2, 2, 3, 232, 3, 2, 2, 2, 5, 238, 3, 2, 2, 2, 7, 252, 3, 2, 2, 2, 9, 265, 3, 2, 2, 2, 11, 267, 3, 2, 2, 2, 13, 269, 3, 2, 2, 2, 15, 271, 3, 2, 2, 2, 17, 273, 3, 2, 2, 2, 19, 275, 3, 2, 2, 2, 21, 277, 3, 2, 2, 2, 23, 279, 3, 2, 2, 2, 25, 281, 3, 2, 2, 2, 27, 283, 3, 2, 2, 2, 29, 285, 3, 2, 2, 2, 31, 287, 3, 2, 2, 2, 33, 289, 3, 2, 2, 2, 35, 291, 3, 2, 2, 2, 37, 293, 3, 2, 2, 2, 39, 295, 3, 2, 2, 2, 41, 297, 3, 2, 2, 2, 43, 299, 3, 2, 2, 2, 45, 301, 3, 2, 2, 2, 47, 303, 3, 2, 2, 2, 49, 305, 3, 2, 2, 2, 51, 307, 3, 2, 2, 2, 53, 309, 3, 2, 2, 2, 55, 311, 3, 2, 2, 2, 57, 313, 3, 2, 2, 2, 59, 315, 3, 2, 2, 2, 61, 317, 3, 2, 2, 2, 63, 319, 3, 2, 2, 2, 65, 321, 3, 2, 2, 2, 67, 323, 3, 2, 2, 2, 69, 334, 3, 2, 2, 2, 71, 345, 3, 2, 2, 2, 73, 358, 3, 2, 2, 2, 75, 363, 3, 2, 2, 2, 77, 369, 3, 2, 2, 2, 79, 375, 3, 2, 2, 2, 81, 387, 3, 2, 2, 2, 83, 389, 3, 2, 2, 2, 85, 391, 3, 2, 2, 2, 87, 394, 3, 2, 2, 2, 89, 396, 3, 2, 2, 2, 91, 399, 3, 2, 2, 2, 93, 401, 3, 2, 2, 2, 95, 403, 3, 2, 2, 2, 97, 405, 3, 2, 2, 2, 99, 407, 3, 2, 2, 2, 101, 409, 3, 2, 2, 2, 103, 419, 3, 2, 2, 2, 105, 423, 3, 2, 2, 2, 107, 451, 3, 2, 2, 2, 109, 466, 3, 2, 2, 2, 111, 469, 3, 2, 2, 2, 113, 471, 3, 2, 2, 2, 115, 473, 3, 2, 2, 2, 117, 475, 3, 2, 2, 2, 119, 482, 3, 2, 2, 2, 121, 489, 3, 2, 2, 2, 123, 491, 3, 2, 2, 2, 125, 494, 3, 2, 2, 2, 127, 497, 3, 2, 2, 2, 129, 500, 3, 2, 2, 2, 131, 503, 3, 2, 2, 2, 133, 505, 3, 2, 2, 2, 135, 507, 3, 2, 2, 2, 137, 510, 3, 2, 2, 2, 139, 513, 3, 2, 2, 2, 141, 515, 3, 2, 2, 2, 143, 517, 3, 2, 2, 2, 145, 520, 3, 2, 2, 2, 147, 522, 3, 2, 2, 2, 149, 525, 3, 2, 2, 2, 151, 528, 3, 2, 2, 2, 153, 530, 3, 2, 2, 2, 155, 533, 3, 2, 2, 2, 157, 536, 3, 2, 2, 2, 159, 540, 3, 2, 2, 2, 161, 543, 3, 2, 2, 2, 163, 546, 3, 2, 2, 2, 165, 550, 3, 2, 2, 2, 167, 554, 3, 2, 2, 2, 169, 556, 3, 2, 2, 2, 171, 559, 3, 2, 2, 2, 173, 562, 3, 2, 2, 2, 175, 565, 3, 2, 2, 2, 177, 568, 3, 2, 2, 2, 179, 572, 3, 2, 2, 2, 181, 575, 3, 2, 2, 2, 183, 579, 3, 2, 2, 2, 185, 583, 3, 2, 2, 2, 187, 585, 3, 2, 2, 2, 189, 587, 3, 2, 2, 2, 191, 589, 3, 2, 2, 2, 193, 592, 3, 2, 2, 2, 195, 596, 3, 2, 2, 2, 197, 599, 3, 2, 2, 2, 199, 601, 3, 2, 2, 2, 201, 603, 3, 2, 2, 2, 203, 606, 3, 2, 2, 2, 205, 609, 3, 2, 2, 2, 207, 612, 3, 2, 2, 2, 209, 615, 3, 2, 2, 2, 211, 617, 3, 2, 2, 2, 213, 619, 3, 2, 2, 2, 215, 622, 3, 2, 2, 2, 217, 627, 3, 2, 2, 2, 219, 631, 3, 2, 2, 2, 221, 636, 3, 2, 2, 2, 223, 640, 3, 2, 2, 2, 225, 643, 3, 2, 2, 2, 227, 646, 3, 2, 2, 2, 229, 648, 3, 2, 2, 2, 231, 233, 9, 2, 2, 2, 232, 231, 3, 2, 2, 2, 233, 234, 3, 2, 2, 2, 234, 232, 3, 2, 2, 2, 234, 235, 3, 2, 2, 2, 235, 236, 3, 2, 2, 2, 236, 237, 8, 2, 2, 2, 237, 4, 3, 2, 2, 2, 238, 239, 7, 49, 2, 2, 239, 240, 7, 44, 2, 2, 240, 244, 3, 2, 2, 2, 241, 243, 11, 2, 2, 2, 242, 241, 3, 2, 2, 2, 243, 246, 3, 2, 2, 2, 244, 245, 3, 2, 2, 2, 244, 242, 3, 2, 2, 2, 245, 247, 3, 2, 2, 2, 246, 244, 3, 2, 2, 2, 247, 248, 7, 44, 2, 2, 248, 249, 7, 49, 2, 2, 249, 250, 3, 2, 2, 2, 250, 251, 8, 3, 2, 2, 251, 6, 3, 2, 2, 2, 252, 253, 7, 49, 2, 2, 253, 254, 7, 49, 2, 2, 254, 258, 3, 2, 2, 2, 255, 257, 11, 2, 2, 2, 256, 255, 3, 2, 2, 2, 257, 260, 3, 2, 2, 2, 258, 259, 3, 2, 2, 2, 258, 256, 3, 2, 2, 2, 259, 261, 3, 2, 2, 2, 260, 258, 3, 2, 2, 2, 261, 262, 7, 12, 2, 2, 262, 263, 3, 2, 2, 2, 263, 264, 8, 4, 2, 2, 264, 8, 3, 2, 2, 2, 265, 266, 9, 3, 2, 2, 266, 10, 3, 2, 2, 2, 267, 268, 9, 4, 2, 2, 268, 12, 3, 2, 2, 2, 269, 270, 9, 5, 2, 2, 270, 14, 3, 2, 2, 2, 271, 272, 9, 6, 2, 2, 272, 16, 3, 2, 2, 2, 273, 274, 9, 7, 2, 2, 274, 18, 3, 2, 2, 2, 275, 276, 9, 8, 2, 2, 276, 20, 3, 2, 2, 2, 277, 278, 9, 9, 2, 2, 278, 22, 3, 2, 2, 2, 279, 280, 9, 10, 2, 2, 280, 24, 3, 2, 2, 2, 281, 282, 9, 11, 2, 2, 282, 26, 3, 2, 2, 2, 283, 284, 9, 12, 2, 2, 284, 28, 3, 2, 2, 2, 285, 286, 9, 13, 2, 2, 286, 30, 3, 2, 2, 2, 287, 288, 9, 14, 2, 2, 288, 32, 3, 2, 2, 2, 289, 290, 9, 15, 2, 2, 290, 34, 3, 2, 2, 2, 291, 292, 9, 16, 2, 2, 292, 36, 3, 2, 2, 2, 293, 294, 9, 17, 2, 2, 294, 38, 3, 2, 2, 2, 295, 296, 9, 18, 2, 2, 296, 40, 3, 2, 2, 2, 297, 298, 9, 19, 2, 2, 298, 42, 3, 2, 2, 2, 299, 300, 9, 20, 2, 2, 300, 44, 3, 2, 2, 2, 301, 302, 9, 21, 2, 2, 302, 46, 3, 2, 2, 2, 303, 304, 9, 22, 2, 2, 304, 48, 3, 2, 2, 2, 305, 306, 9, 23, 2, 2, 306, 50, 3, 2, 2, 2, 307, 308, 9, 24, 2, 2, 308, 52, 3, 2, 2, 2, 309, 310, 9, 25, 2, 2, 310, 54, 3, 2, 2, 2, 311, 312, 9, 26, 2, 2, 312, 56, 3, 2, 2, 2, 313, 314, 9, 27, 2, 2, 314, 58, 3, 2, 2, 2, 315, 316, 9, 28, 2, 2, 316, 60, 3, 2, 2, 2, 317, 318, 7, 97, 2, 2, 318, 62, 3, 2, 2, 2, 319, 320, 9, 29, 2, 2, 320, 64, 3, 2, 2, 2, 321, 322, 9, 30, 2, 2, 322, 66, 3, 2, 2, 2, 323, 329, 7, 36, 2, 2, 324, 325, 7, 94, 2, 2, 325, 328, 11, 2, 2, 2, 326, 328, 10, 31, 2, 2, 327, 324, 3, 2, 2, 2, 327, 326, 3, 2, 2, 2, 328, 331, 3, 2, 2, 2, 329, 327, 3, 2, 2, 2, 329, 330, 3, 2, 2, 2, 330, 332, 3, 2, 2, 2, 331, 329, 3, 2, 2, 2, 332, 333, 7, 36, 2, 2, 333, 68, 3, 2, 2, 2, 334, 340, 7, 41, 2, 2, 335, 336, 7, 94, 2, 2, 336, 339, 11, 2, 2, 2, 337, 339, 10, 32, 2, 2, 338, 335, 3, 2, 2, 2, 338, 337, 3, 2, 2, 2, 339, 342, 3, 2, 2, 2, 340, 338, 3, 2, 2, 2, 340, 341, 3, 2, 2, 2, 341, 343, 3, 2, 2, 2, 342, 340, 3, 2, 2, 2, 343, 344, 7, 41, 2, 2, 344, 70, 3, 2, 2, 2, 345, 353, 7, 98, 2, 2, 346, 347, 7, 94, 2, 2, 347, 352, 11, 2, 2, 2, 348, 349, 7, 98, 2, 2, 349, 352, 7, 98, 2, 2, 350, 352, 10, 33, 2, 2, 351, 346, 3, 2, 2, 2, 351, 348, 3, 2, 2, 2, 351, 350, 3, 2, 2, 2, 352, 355, 3, 2, 2, 2, 353, 351, 3, 2, 2, 2, 353, 354, 3, 2, 2, 2, 354, 356, 3, 2, 2, 2, 355, 353, 3, 2, 2, 2, 356, 357, 7, 98, 2, 2, 357, 72, 3, 2, 2, 2, 358, 359, 5, 47, 24, 2, 359, 360, 5, 43, 22, 2, 360, 361, 5, 49, 25, 2, 361, 362, 5, 17, 9, 2, 362, 74, 3, 2, 2, 2, 363, 364, 5, 19, 10, 2, 364, 365, 5, 9, 5, 2, 365, 366, 5, 31, 16, 2, 366, 367, 5, 45, 23, 2, 367, 368, 5, 17, 9, 2, 368, 76, 3, 2, 2, 2, 369, 370, 5, 19, 10, 2, 370, 371, 5, 25, 13, 2, 371, 372, 5, 17, 9, 2, 372, 373, 5, 31, 16, 2, 373, 374, 5, 15, 8, 2, 374, 78, 3, 2, 2, 2, 375, 376, 5, 19, 10, 2, 376, 377, 5, 25, 13, 2, 377, 378, 5, 17, 9, 2, 378, 379, 5, 31, 16, 2, 379, 380, 5, 15, 8, 2, 380, 381, 5, 61, 31, 2, 381, 382, 5, 11, 6, 2, 382, 383, 5, 57, 29, 2, 383, 384, 5, 61, 31, 2, 384, 385, 5, 25, 13, 2, 385, 386, 5, 15, 8, 2, 386, 80, 3, 2, 2, 2, 387, 388, 7, 46, 2, 2, 388, 82, 3, 2, 2, 2, 389, 390, 7, 60, 2, 2, 390, 84, 3, 2, 2, 2, 391, 392, 7, 60, 2, 2, 392, 393, 7, 60, 2, 2, 393, 86, 3, 2, 2, 2, 394, 395, 7, 38, 2, 2, 395, 88, 3, 2, 2, 2, 396, 397, 7, 38, 2, 2, 397, 398, 7, 38, 2, 2, 398, 90, 3, 2, 2, 2, 399, 400, 7, 44, 2, 2, 400, 92, 3, 2, 2, 2, 401, 402, 7, 42, 2, 2, 402, 94, 3, 2, 2, 2, 403, 404, 7, 43, 2, 2, 404, 96, 3, 2, 2, 2, 405, 406, 7, 93, 2, 2, 406, 98, 3, 2, 2, 2, 407, 408, 7, 95, 2, 2, 408, 100, 3, 2, 2, 2, 409, 410, 5, 11, 6, 2, 410, 414, 7, 41, 2, 2, 411, 413, 4, 50, 51, 2, 412, 411, 3, 2, 2, 2, 413, 416, 3, 2, 2, 2, 414, 412, 3, 2, 2, 2, 414, 415, 3, 2, 2, 2, 415, 417, 3, 2, 2, 2, 416, 414, 3, 2, 2, 2, 417, 418, 7, 41, 2, 2, 418, 102, 3, 2, 2, 2, 419, 420, 5, 17, 9, 2, 420, 421, 5, 69, 35, 2, 421, 104, 3, 2, 2, 2, 422, 424, 7, 47, 2, 2, 423, 422, 3, 2, 2, 2, 423, 424, 3, 2, 2, 2, 424, 426, 3, 2, 2, 2, 425, 427, 5, 65, 33, 2, 426, 425, 3, 2, 2, 2, 427, 428, 3, 2, 2, 2, 428, 426, 3, 2, 2, 2, 428, 429, 3, 2, 2, 2, 429, 430, 3, 2, 2, 2, 430, 432, 7, 48, 2, 2, 431, 433, 5, 65, 33, 2, 432, 431, 3, 2, 2, 2, 433, 434, 3, 2, 2, 2, 434, 432, 3, 2, 2, 2, 434, 435, 3, 2, 2, 2, 435, 448, 3, 2, 2, 2, 436, 440, 5, 17, 9, 2, 437, 439, 7, 47, 2, 2, 438, 437, 3, 2, 2, 2, 439, 442, 3, 2, 2, 2, 440, 438, 3, 2, 2, 2, 440, 441, 3, 2, 2, 2, 441, 444, 3, 2, 2, 2, 442, 440, 3, 2, 2, 2, 443, 445, 5, 65, 33, 2, 444, 443, 3, 2, 2, 2, 445, 446, 3, 2, 2, 2, 446, 444, 3, 2, 2, 2, 446, 447, 3, 2, 2, 2, 447, 449, 3, 2, 2, 2, 448, 436, 3, 2, 2, 2, 448, 449, 3, 2, 2, 2, 449, 106, 3, 2, 2, 2, 450, 452, 7, 47, 2, 2, 451, 450, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 454, 3, 2, 2, 2, 453, 455, 5, 65, 33, 2, 454, 453, 3, 2, 2, 2, 455, 456, 3, 2, 2, 2, 456, 454, 3, 2, 2, 2, 456, 457, 3, 2, 2, 2, 457, 464, 3, 2, 2, 2, 458, 460, 5, 17, 9, 2, 459, 461, 5, 65, 33, 2, 460, 459, 3, 2, 2, 2, 461, 462, 3, 2, 2, 2, 462, 460, 3, 2, 2, 2, 462, 463, 3, 2, 2, 2, 463, 465, 3, 2, 2, 2, 464, 458, 3, 2, 2, 2, 464, 465, 3, 2, 2, 2, 465, 108, 3, 2, 2, 2, 466, 467, 7, 122, 2, 2, 467, 468, 5, 69, 35, 2, 468, 110, 3, 2, 2, 2, 469, 470, 7, 48, 2, 2, 470, 112, 3, 2, 2, 2, 471, 472, 5, 69, 35, 2, 472, 114, 3, 2, 2, 2, 473, 474, 5, 67, 34, 2, 474, 116, 3, 2, 2, 2, 475, 479, 9, 34, 2, 2, 476, 478, 9, 35, 2, 2, 477, 476, 3, 2, 2, 2, 478, 481, 3, 2, 2, 2, 479, 477, 3, 2, 2, 2, 479, 480, 3, 2, 2, 2, 480, 118, 3, 2, 2, 2, 481, 479, 3, 2, 2, 2, 482, 486, 9, 36, 2, 2, 483, 485, 9, 37, 2, 2, 484, 483, 3, 2, 2, 2, 485, 488, 3, 2, 2, 2, 486, 484, 3, 2, 2, 2, 486, 487, 3, 2, 2, 2, 487, 120, 3, 2, 2, 2, 488, 486, 3, 2, 2, 2, 489, 490, 7, 40, 2, 2, 490, 122, 3, 2, 2, 2, 491, 492, 7, 40, 2, 2, 492, 493, 7, 40, 2, 2, 493, 124, 3, 2, 2, 2, 494, 495, 7, 40, 2, 2, 495, 496, 7, 62, 2, 2, 496, 126, 3, 2, 2, 2, 497, 498, 7, 66, 2, 2, 498, 499, 7, 66, 2, 2, 499, 128, 3, 2, 2, 2, 500, 501, 7, 66, 2, 2, 501, 502, 7, 64, 2, 2, 502, 130, 3, 2, 2, 2, 503, 504, 7, 66, 2, 2, 504, 132, 3, 2, 2, 2, 505, 506, 7, 35, 2, 2, 506, 134, 3, 2, 2, 2, 507, 508, 7, 35, 2, 2, 508, 509, 7, 35, 2, 2, 509, 136, 3, 2, 2, 2, 510, 511, 7, 35, 2, 2, 511, 512, 7, 63, 2, 2, 512, 138, 3, 2, 2, 2, 513, 514, 7, 96, 2, 2, 514, 140, 3, 2, 2, 2, 515, 516, 7, 63, 2, 2, 516, 142, 3, 2, 2, 2, 517, 518, 7, 63, 2, 2, 518, 519, 7, 64, 2, 2, 519, 144, 3, 2, 2, 2, 520, 521, 7, 64, 2, 2, 521, 146, 3, 2, 2, 2, 522, 523, 7, 64, 2, 2, 523, 524, 7, 63, 2, 2, 524, 148, 3, 2, 2, 2, 525, 526, 7, 64, 2, 2, 526, 527, 7, 64, 2, 2, 527, 150, 3, 2, 2, 2, 528, 529, 7, 37, 2, 2, 529, 152, 3, 2, 2, 2, 530, 531, 7, 37, 2, 2, 531, 532, 7, 63, 2, 2, 532, 154, 3, 2, 2, 2, 533, 534, 7, 37, 2, 2, 534, 535, 7, 64, 2, 2, 535, 156, 3, 2, 2, 2, 536, 537, 7, 37, 2, 2, 537, 538, 7, 64, 2, 2, 538, 539, 7, 64, 2, 2, 539, 158, 3, 2, 2, 2, 540, 541, 7, 37, 2, 2, 541, 542, 7, 37, 2, 2, 542, 160, 3, 2, 2, 2, 543, 544, 7, 47, 2, 2, 544, 545, 7, 64, 2, 2, 545, 162, 3, 2, 2, 2, 546, 547, 7, 47, 2, 2, 547, 548, 7, 64, 2, 2, 548, 549, 7, 64, 2, 2, 549, 164, 3, 2, 2, 2, 550, 551, 7, 47, 2, 2, 551, 552, 7, 126, 2, 2, 552, 553, 7, 47, 2, 2, 553, 166, 3, 2, 2, 2, 554, 555, 7, 62, 2, 2, 555, 168, 3, 2, 2, 2, 556, 557, 7, 62, 2, 2, 557, 558, 7, 63, 2, 2, 558, 170, 3, 2, 2, 2, 559, 560, 7, 62, 2, 2, 560, 561, 7, 66, 2, 2, 561, 172, 3, 2, 2, 2, 562, 563, 7, 62, 2, 2, 563, 564, 7, 96, 2, 2, 564, 174, 3, 2, 2, 2, 565, 566, 7, 62, 2, 2, 566, 567, 7, 64, 2, 2, 567, 176, 3, 2, 2, 2, 568, 569, 7, 62, 2, 2, 569, 570, 7, 47, 2, 2, 570, 571, 7, 64, 2, 2, 571, 178, 3, 2, 2, 2, 572, 573, 7, 62, 2, 2, 573, 574, 7, 62, 2, 2, 574, 180, 3, 2, 2, 2, 575, 576, 7, 62, 2, 2, 576, 577, 7, 62, 2, 2, 577, 578, 7, 63, 2, 2, 578, 182, 3, 2, 2, 2, 579, 580, 7, 62, 2, 2, 580, 581, 7, 65, 2, 2, 581, 582, 7, 64, 2, 2, 582, 184, 3, 2, 2, 2, 583, 584, 7, 47, 2, 2, 584, 186, 3, 2, 2, 2, 585, 586, 7, 39, 2, 2, 586, 188, 3, 2, 2, 2, 587, 588, 7, 126, 2, 2, 588, 190, 3, 2, 2, 2, 589, 590, 7, 126, 2, 2, 590, 591, 7, 126, 2, 2, 591, 192, 3, 2, 2, 2, 592, 593, 7, 126, 2, 2, 593, 594, 7, 126, 2, 2, 594, 595, 7, 49, 2, 2, 595, 194, 3, 2, 2, 2, 596, 597, 7, 126, 2, 2, 597, 598, 7, 49, 2, 2, 598, 196, 3, 2, 2, 2, 599, 600, 7, 45, 2, 2, 600, 198, 3, 2, 2, 2, 601, 602, 7, 65, 2, 2, 602, 200, 3, 2, 2, 2, 603, 604, 7, 65, 2, 2, 604, 605, 7, 40, 2, 2, 605, 202, 3, 2, 2, 2, 606, 607, 7, 65, 2, 2, 607, 608, 7, 37, 2, 2, 608, 204, 3, 2, 2, 2, 609, 610, 7, 65, 2, 2, 610, 611, 7, 47, 2, 2, 611, 206, 3, 2, 2, 2, 612, 613, 7, 65, 2, 2, 613, 614, 7, 126, 2, 2, 614, 208, 3, 2, 2, 2, 615, 616, 7, 49, 2, 2, 616, 210, 3, 2, 2, 2, 617, 618, 7, 128, 2, 2, 618, 212, 3, 2, 2, 2, 619, 620, 7, 128, 2, 2, 620, 621, 7, 63, 2, 2, 621, 214, 3, 2, 2, 2, 622, 623, 7, 128, 2, 2, 623, 624, 7, 64, 2, 2, 624, 625, 7, 63, 2, 2, 625, 626, 7, 128, 2, 2, 626, 216, 3, 2, 2, 2, 627, 628, 7, 128, 2, 2, 628, 629, 7, 64, 2, 2, 629, 630, 7, 128, 2, 2, 630, 218, 3, 2, 2, 2, 631, 632, 7, 128, 2, 2, 632, 633, 7, 62, 2, 2, 633, 634, 7, 63, 2, 2, 634, 635, 7, 128, 2, 2, 635, 220, 3, 2, 2, 2, 636, 637, 7, 128, 2, 2, 637, 638, 7, 62, 2, 2, 638, 639, 7, 128, 2, 2, 639, 222, 3, 2, 2, 2, 640, 641, 7, 128, 2, 2, 641, 642, 7, 44, 2, 2, 642, 224, 3, 2, 2, 2, 643, 644, 7, 128, 2, 2, 644, 645, 7, 128, 2, 2, 645, 226, 3, 2, 2, 2, 646, 647, 7, 61, 2, 2, 647, 228, 3, 2, 2, 2, 648, 649, 11, 2, 2, 2, 649, 230, 3, 2, 2, 2, 25, 2, 234, 244, 258, 327, 329, 338, 340, 351, 353, 414, 423, 428, 434, 440, 446, 448, 451, 456, 462, 464, 479, 486, 3, 2, 3, 2] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 84, 642, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 7, 31, 294, 10, 31, 12, 31, 14, 31, 297, 11, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 7, 32, 305, 10, 32, 12, 32, 14, 32, 308, 11, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 7, 33, 318, 10, 33, 12, 33, 14, 33, 321, 11, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 7, 34, 329, 10, 34, 12, 34, 14, 34, 332, 11, 34, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 7, 35, 341, 10, 35, 12, 35, 14, 35, 344, 11, 35, 3, 36, 6, 36, 347, 10, 36, 13, 36, 14, 36, 348, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 7, 51, 405, 10, 51, 12, 51, 14, 51, 408, 11, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 5, 53, 416, 10, 53, 3, 53, 6, 53, 419, 10, 53, 13, 53, 14, 53, 420, 3, 53, 3, 53, 6, 53, 425, 10, 53, 13, 53, 14, 53, 426, 3, 53, 3, 53, 7, 53, 431, 10, 53, 12, 53, 14, 53, 434, 11, 53, 3, 53, 6, 53, 437, 10, 53, 13, 53, 14, 53, 438, 5, 53, 441, 10, 53, 3, 54, 5, 54, 444, 10, 54, 3, 54, 6, 54, 447, 10, 54, 13, 54, 14, 54, 448, 3, 54, 3, 54, 6, 54, 453, 10, 54, 13, 54, 14, 54, 454, 5, 54, 457, 10, 54, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 7, 59, 470, 10, 59, 12, 59, 14, 59, 473, 11, 59, 3, 60, 3, 60, 7, 60, 477, 10, 60, 12, 60, 14, 60, 480, 11, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 82, 3, 83, 3, 83, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 96, 3, 97, 3, 97, 3, 97, 3, 97, 3, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 101, 3, 102, 3, 102, 3, 102, 3, 103, 3, 103, 3, 103, 3, 104, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 107, 3, 108, 3, 108, 3, 108, 3, 108, 3, 108, 3, 109, 3, 109, 3, 109, 3, 109, 3, 110, 3, 110, 3, 110, 3, 110, 3, 110, 3, 111, 3, 111, 3, 111, 3, 111, 3, 112, 3, 112, 3, 112, 3, 113, 3, 113, 3, 113, 3, 114, 3, 114, 3, 115, 3, 115, 2, 2, 116, 3, 2, 5, 2, 7, 2, 9, 2, 11, 2, 13, 2, 15, 2, 17, 2, 19, 2, 21, 2, 23, 2, 25, 2, 27, 2, 29, 2, 31, 2, 33, 2, 35, 2, 37, 2, 39, 2, 41, 2, 43, 2, 45, 2, 47, 2, 49, 2, 51, 2, 53, 2, 55, 2, 57, 2, 59, 2, 61, 2, 63, 2, 65, 2, 67, 3, 69, 4, 71, 5, 73, 6, 75, 7, 77, 8, 79, 9, 81, 10, 83, 11, 85, 12, 87, 13, 89, 14, 91, 15, 93, 16, 95, 17, 97, 18, 99, 19, 101, 20, 103, 21, 105, 22, 107, 23, 109, 24, 111, 25, 113, 26, 115, 27, 117, 28, 119, 29, 121, 30, 123, 31, 125, 32, 127, 33, 129, 34, 131, 35, 133, 36, 135, 37, 137, 38, 139, 39, 141, 40, 143, 41, 145, 42, 147, 43, 149, 44, 151, 45, 153, 46, 155, 47, 157, 48, 159, 49, 161, 50, 163, 51, 165, 52, 167, 53, 169, 54, 171, 55, 173, 56, 175, 57, 177, 58, 179, 59, 181, 60, 183, 61, 185, 62, 187, 63, 189, 64, 191, 65, 193, 66, 195, 67, 197, 68, 199, 69, 201, 70, 203, 71, 205, 72, 207, 73, 209, 74, 211, 75, 213, 76, 215, 77, 217, 78, 219, 79, 221, 80, 223, 81, 225, 82, 227, 83, 229, 84, 3, 2, 39, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 4, 2, 50, 59, 67, 72, 3, 2, 50, 59, 4, 2, 36, 36, 94, 94, 4, 2, 41, 41, 94, 94, 4, 2, 94, 94, 98, 98, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 6, 2, 67, 92, 97, 97, 99, 124, 163, 1, 7, 2, 50, 59, 67, 92, 97, 97, 99, 124, 163, 1, 2, 632, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 157, 3, 2, 2, 2, 2, 159, 3, 2, 2, 2, 2, 161, 3, 2, 2, 2, 2, 163, 3, 2, 2, 2, 2, 165, 3, 2, 2, 2, 2, 167, 3, 2, 2, 2, 2, 169, 3, 2, 2, 2, 2, 171, 3, 2, 2, 2, 2, 173, 3, 2, 2, 2, 2, 175, 3, 2, 2, 2, 2, 177, 3, 2, 2, 2, 2, 179, 3, 2, 2, 2, 2, 181, 3, 2, 2, 2, 2, 183, 3, 2, 2, 2, 2, 185, 3, 2, 2, 2, 2, 187, 3, 2, 2, 2, 2, 189, 3, 2, 2, 2, 2, 191, 3, 2, 2, 2, 2, 193, 3, 2, 2, 2, 2, 195, 3, 2, 2, 2, 2, 197, 3, 2, 2, 2, 2, 199, 3, 2, 2, 2, 2, 201, 3, 2, 2, 2, 2, 203, 3, 2, 2, 2, 2, 205, 3, 2, 2, 2, 2, 207, 3, 2, 2, 2, 2, 209, 3, 2, 2, 2, 2, 211, 3, 2, 2, 2, 2, 213, 3, 2, 2, 2, 2, 215, 3, 2, 2, 2, 2, 217, 3, 2, 2, 2, 2, 219, 3, 2, 2, 2, 2, 221, 3, 2, 2, 2, 2, 223, 3, 2, 2, 2, 2, 225, 3, 2, 2, 2, 2, 227, 3, 2, 2, 2, 2, 229, 3, 2, 2, 2, 3, 231, 3, 2, 2, 2, 5, 233, 3, 2, 2, 2, 7, 235, 3, 2, 2, 2, 9, 237, 3, 2, 2, 2, 11, 239, 3, 2, 2, 2, 13, 241, 3, 2, 2, 2, 15, 243, 3, 2, 2, 2, 17, 245, 3, 2, 2, 2, 19, 247, 3, 2, 2, 2, 21, 249, 3, 2, 2, 2, 23, 251, 3, 2, 2, 2, 25, 253, 3, 2, 2, 2, 27, 255, 3, 2, 2, 2, 29, 257, 3, 2, 2, 2, 31, 259, 3, 2, 2, 2, 33, 261, 3, 2, 2, 2, 35, 263, 3, 2, 2, 2, 37, 265, 3, 2, 2, 2, 39, 267, 3, 2, 2, 2, 41, 269, 3, 2, 2, 2, 43, 271, 3, 2, 2, 2, 45, 273, 3, 2, 2, 2, 47, 275, 3, 2, 2, 2, 49, 277, 3, 2, 2, 2, 51, 279, 3, 2, 2, 2, 53, 281, 3, 2, 2, 2, 55, 283, 3, 2, 2, 2, 57, 285, 3, 2, 2, 2, 59, 287, 3, 2, 2, 2, 61, 289, 3, 2, 2, 2, 63, 300, 3, 2, 2, 2, 65, 311, 3, 2, 2, 2, 67, 324, 3, 2, 2, 2, 69, 336, 3, 2, 2, 2, 71, 346, 3, 2, 2, 2, 73, 350, 3, 2, 2, 2, 75, 355, 3, 2, 2, 2, 77, 361, 3, 2, 2, 2, 79, 367, 3, 2, 2, 2, 81, 379, 3, 2, 2, 2, 83, 381, 3, 2, 2, 2, 85, 383, 3, 2, 2, 2, 87, 386, 3, 2, 2, 2, 89, 388, 3, 2, 2, 2, 91, 391, 3, 2, 2, 2, 93, 393, 3, 2, 2, 2, 95, 395, 3, 2, 2, 2, 97, 397, 3, 2, 2, 2, 99, 399, 3, 2, 2, 2, 101, 401, 3, 2, 2, 2, 103, 411, 3, 2, 2, 2, 105, 415, 3, 2, 2, 2, 107, 443, 3, 2, 2, 2, 109, 458, 3, 2, 2, 2, 111, 461, 3, 2, 2, 2, 113, 463, 3, 2, 2, 2, 115, 465, 3, 2, 2, 2, 117, 467, 3, 2, 2, 2, 119, 474, 3, 2, 2, 2, 121, 481, 3, 2, 2, 2, 123, 483, 3, 2, 2, 2, 125, 486, 3, 2, 2, 2, 127, 489, 3, 2, 2, 2, 129, 492, 3, 2, 2, 2, 131, 495, 3, 2, 2, 2, 133, 497, 3, 2, 2, 2, 135, 499, 3, 2, 2, 2, 137, 502, 3, 2, 2, 2, 139, 505, 3, 2, 2, 2, 141, 507, 3, 2, 2, 2, 143, 509, 3, 2, 2, 2, 145, 512, 3, 2, 2, 2, 147, 514, 3, 2, 2, 2, 149, 517, 3, 2, 2, 2, 151, 520, 3, 2, 2, 2, 153, 522, 3, 2, 2, 2, 155, 525, 3, 2, 2, 2, 157, 528, 3, 2, 2, 2, 159, 532, 3, 2, 2, 2, 161, 535, 3, 2, 2, 2, 163, 538, 3, 2, 2, 2, 165, 542, 3, 2, 2, 2, 167, 546, 3, 2, 2, 2, 169, 548, 3, 2, 2, 2, 171, 551, 3, 2, 2, 2, 173, 554, 3, 2, 2, 2, 175, 557, 3, 2, 2, 2, 177, 560, 3, 2, 2, 2, 179, 564, 3, 2, 2, 2, 181, 567, 3, 2, 2, 2, 183, 571, 3, 2, 2, 2, 185, 575, 3, 2, 2, 2, 187, 577, 3, 2, 2, 2, 189, 579, 3, 2, 2, 2, 191, 581, 3, 2, 2, 2, 193, 584, 3, 2, 2, 2, 195, 588, 3, 2, 2, 2, 197, 591, 3, 2, 2, 2, 199, 593, 3, 2, 2, 2, 201, 595, 3, 2, 2, 2, 203, 598, 3, 2, 2, 2, 205, 601, 3, 2, 2, 2, 207, 604, 3, 2, 2, 2, 209, 607, 3, 2, 2, 2, 211, 609, 3, 2, 2, 2, 213, 611, 3, 2, 2, 2, 215, 614, 3, 2, 2, 2, 217, 619, 3, 2, 2, 2, 219, 623, 3, 2, 2, 2, 221, 628, 3, 2, 2, 2, 223, 632, 3, 2, 2, 2, 225, 635, 3, 2, 2, 2, 227, 638, 3, 2, 2, 2, 229, 640, 3, 2, 2, 2, 231, 232, 9, 2, 2, 2, 232, 4, 3, 2, 2, 2, 233, 234, 9, 3, 2, 2, 234, 6, 3, 2, 2, 2, 235, 236, 9, 4, 2, 2, 236, 8, 3, 2, 2, 2, 237, 238, 9, 5, 2, 2, 238, 10, 3, 2, 2, 2, 239, 240, 9, 6, 2, 2, 240, 12, 3, 2, 2, 2, 241, 242, 9, 7, 2, 2, 242, 14, 3, 2, 2, 2, 243, 244, 9, 8, 2, 2, 244, 16, 3, 2, 2, 2, 245, 246, 9, 9, 2, 2, 246, 18, 3, 2, 2, 2, 247, 248, 9, 10, 2, 2, 248, 20, 3, 2, 2, 2, 249, 250, 9, 11, 2, 2, 250, 22, 3, 2, 2, 2, 251, 252, 9, 12, 2, 2, 252, 24, 3, 2, 2, 2, 253, 254, 9, 13, 2, 2, 254, 26, 3, 2, 2, 2, 255, 256, 9, 14, 2, 2, 256, 28, 3, 2, 2, 2, 257, 258, 9, 15, 2, 2, 258, 30, 3, 2, 2, 2, 259, 260, 9, 16, 2, 2, 260, 32, 3, 2, 2, 2, 261, 262, 9, 17, 2, 2, 262, 34, 3, 2, 2, 2, 263, 264, 9, 18, 2, 2, 264, 36, 3, 2, 2, 2, 265, 266, 9, 19, 2, 2, 266, 38, 3, 2, 2, 2, 267, 268, 9, 20, 2, 2, 268, 40, 3, 2, 2, 2, 269, 270, 9, 21, 2, 2, 270, 42, 3, 2, 2, 2, 271, 272, 9, 22, 2, 2, 272, 44, 3, 2, 2, 2, 273, 274, 9, 23, 2, 2, 274, 46, 3, 2, 2, 2, 275, 276, 9, 24, 2, 2, 276, 48, 3, 2, 2, 2, 277, 278, 9, 25, 2, 2, 278, 50, 3, 2, 2, 2, 279, 280, 9, 26, 2, 2, 280, 52, 3, 2, 2, 2, 281, 282, 9, 27, 2, 2, 282, 54, 3, 2, 2, 2, 283, 284, 7, 97, 2, 2, 284, 56, 3, 2, 2, 2, 285, 286, 9, 28, 2, 2, 286, 58, 3, 2, 2, 2, 287, 288, 9, 29, 2, 2, 288, 60, 3, 2, 2, 2, 289, 295, 7, 36, 2, 2, 290, 291, 7, 94, 2, 2, 291, 294, 11, 2, 2, 2, 292, 294, 10, 30, 2, 2, 293, 290, 3, 2, 2, 2, 293, 292, 3, 2, 2, 2, 294, 297, 3, 2, 2, 2, 295, 293, 3, 2, 2, 2, 295, 296, 3, 2, 2, 2, 296, 298, 3, 2, 2, 2, 297, 295, 3, 2, 2, 2, 298, 299, 7, 36, 2, 2, 299, 62, 3, 2, 2, 2, 300, 306, 7, 41, 2, 2, 301, 302, 7, 94, 2, 2, 302, 305, 11, 2, 2, 2, 303, 305, 10, 31, 2, 2, 304, 301, 3, 2, 2, 2, 304, 303, 3, 2, 2, 2, 305, 308, 3, 2, 2, 2, 306, 304, 3, 2, 2, 2, 306, 307, 3, 2, 2, 2, 307, 309, 3, 2, 2, 2, 308, 306, 3, 2, 2, 2, 309, 310, 7, 41, 2, 2, 310, 64, 3, 2, 2, 2, 311, 319, 7, 98, 2, 2, 312, 313, 7, 94, 2, 2, 313, 318, 11, 2, 2, 2, 314, 315, 7, 98, 2, 2, 315, 318, 7, 98, 2, 2, 316, 318, 10, 32, 2, 2, 317, 312, 3, 2, 2, 2, 317, 314, 3, 2, 2, 2, 317, 316, 3, 2, 2, 2, 318, 321, 3, 2, 2, 2, 319, 317, 3, 2, 2, 2, 319, 320, 3, 2, 2, 2, 320, 322, 3, 2, 2, 2, 321, 319, 3, 2, 2, 2, 322, 323, 7, 98, 2, 2, 323, 66, 3, 2, 2, 2, 324, 325, 7, 49, 2, 2, 325, 326, 7, 44, 2, 2, 326, 330, 3, 2, 2, 2, 327, 329, 11, 2, 2, 2, 328, 327, 3, 2, 2, 2, 329, 332, 3, 2, 2, 2, 330, 328, 3, 2, 2, 2, 330, 331, 3, 2, 2, 2, 331, 333, 3, 2, 2, 2, 332, 330, 3, 2, 2, 2, 333, 334, 7, 44, 2, 2, 334, 335, 7, 49, 2, 2, 335, 68, 3, 2, 2, 2, 336, 337, 7, 49, 2, 2, 337, 338, 7, 49, 2, 2, 338, 342, 3, 2, 2, 2, 339, 341, 10, 33, 2, 2, 340, 339, 3, 2, 2, 2, 341, 344, 3, 2, 2, 2, 342, 340, 3, 2, 2, 2, 342, 343, 3, 2, 2, 2, 343, 70, 3, 2, 2, 2, 344, 342, 3, 2, 2, 2, 345, 347, 9, 34, 2, 2, 346, 345, 3, 2, 2, 2, 347, 348, 3, 2, 2, 2, 348, 346, 3, 2, 2, 2, 348, 349, 3, 2, 2, 2, 349, 72, 3, 2, 2, 2, 350, 351, 5, 41, 21, 2, 351, 352, 5, 37, 19, 2, 352, 353, 5, 43, 22, 2, 353, 354, 5, 11, 6, 2, 354, 74, 3, 2, 2, 2, 355, 356, 5, 13, 7, 2, 356, 357, 5, 3, 2, 2, 357, 358, 5, 25, 13, 2, 358, 359, 5, 39, 20, 2, 359, 360, 5, 11, 6, 2, 360, 76, 3, 2, 2, 2, 361, 362, 5, 13, 7, 2, 362, 363, 5, 19, 10, 2, 363, 364, 5, 11, 6, 2, 364, 365, 5, 25, 13, 2, 365, 366, 5, 9, 5, 2, 366, 78, 3, 2, 2, 2, 367, 368, 5, 13, 7, 2, 368, 369, 5, 19, 10, 2, 369, 370, 5, 11, 6, 2, 370, 371, 5, 25, 13, 2, 371, 372, 5, 9, 5, 2, 372, 373, 5, 55, 28, 2, 373, 374, 5, 5, 3, 2, 374, 375, 5, 51, 26, 2, 375, 376, 5, 55, 28, 2, 376, 377, 5, 19, 10, 2, 377, 378, 5, 9, 5, 2, 378, 80, 3, 2, 2, 2, 379, 380, 7, 46, 2, 2, 380, 82, 3, 2, 2, 2, 381, 382, 7, 60, 2, 2, 382, 84, 3, 2, 2, 2, 383, 384, 7, 60, 2, 2, 384, 385, 7, 60, 2, 2, 385, 86, 3, 2, 2, 2, 386, 387, 7, 38, 2, 2, 387, 88, 3, 2, 2, 2, 388, 389, 7, 38, 2, 2, 389, 390, 7, 38, 2, 2, 390, 90, 3, 2, 2, 2, 391, 392, 7, 44, 2, 2, 392, 92, 3, 2, 2, 2, 393, 394, 7, 42, 2, 2, 394, 94, 3, 2, 2, 2, 395, 396, 7, 43, 2, 2, 396, 96, 3, 2, 2, 2, 397, 398, 7, 93, 2, 2, 398, 98, 3, 2, 2, 2, 399, 400, 7, 95, 2, 2, 400, 100, 3, 2, 2, 2, 401, 402, 5, 5, 3, 2, 402, 406, 7, 41, 2, 2, 403, 405, 4, 50, 51, 2, 404, 403, 3, 2, 2, 2, 405, 408, 3, 2, 2, 2, 406, 404, 3, 2, 2, 2, 406, 407, 3, 2, 2, 2, 407, 409, 3, 2, 2, 2, 408, 406, 3, 2, 2, 2, 409, 410, 7, 41, 2, 2, 410, 102, 3, 2, 2, 2, 411, 412, 5, 11, 6, 2, 412, 413, 5, 63, 32, 2, 413, 104, 3, 2, 2, 2, 414, 416, 7, 47, 2, 2, 415, 414, 3, 2, 2, 2, 415, 416, 3, 2, 2, 2, 416, 418, 3, 2, 2, 2, 417, 419, 5, 59, 30, 2, 418, 417, 3, 2, 2, 2, 419, 420, 3, 2, 2, 2, 420, 418, 3, 2, 2, 2, 420, 421, 3, 2, 2, 2, 421, 422, 3, 2, 2, 2, 422, 424, 7, 48, 2, 2, 423, 425, 5, 59, 30, 2, 424, 423, 3, 2, 2, 2, 425, 426, 3, 2, 2, 2, 426, 424, 3, 2, 2, 2, 426, 427, 3, 2, 2, 2, 427, 440, 3, 2, 2, 2, 428, 432, 5, 11, 6, 2, 429, 431, 7, 47, 2, 2, 430, 429, 3, 2, 2, 2, 431, 434, 3, 2, 2, 2, 432, 430, 3, 2, 2, 2, 432, 433, 3, 2, 2, 2, 433, 436, 3, 2, 2, 2, 434, 432, 3, 2, 2, 2, 435, 437, 5, 59, 30, 2, 436, 435, 3, 2, 2, 2, 437, 438, 3, 2, 2, 2, 438, 436, 3, 2, 2, 2, 438, 439, 3, 2, 2, 2, 439, 441, 3, 2, 2, 2, 440, 428, 3, 2, 2, 2, 440, 441, 3, 2, 2, 2, 441, 106, 3, 2, 2, 2, 442, 444, 7, 47, 2, 2, 443, 442, 3, 2, 2, 2, 443, 444, 3, 2, 2, 2, 444, 446, 3, 2, 2, 2, 445, 447, 5, 59, 30, 2, 446, 445, 3, 2, 2, 2, 447, 448, 3, 2, 2, 2, 448, 446, 3, 2, 2, 2, 448, 449, 3, 2, 2, 2, 449, 456, 3, 2, 2, 2, 450, 452, 5, 11, 6, 2, 451, 453, 5, 59, 30, 2, 452, 451, 3, 2, 2, 2, 453, 454, 3, 2, 2, 2, 454, 452, 3, 2, 2, 2, 454, 455, 3, 2, 2, 2, 455, 457, 3, 2, 2, 2, 456, 450, 3, 2, 2, 2, 456, 457, 3, 2, 2, 2, 457, 108, 3, 2, 2, 2, 458, 459, 7, 122, 2, 2, 459, 460, 5, 63, 32, 2, 460, 110, 3, 2, 2, 2, 461, 462, 7, 48, 2, 2, 462, 112, 3, 2, 2, 2, 463, 464, 5, 63, 32, 2, 464, 114, 3, 2, 2, 2, 465, 466, 5, 61, 31, 2, 466, 116, 3, 2, 2, 2, 467, 471, 9, 35, 2, 2, 468, 470, 9, 36, 2, 2, 469, 468, 3, 2, 2, 2, 470, 473, 3, 2, 2, 2, 471, 469, 3, 2, 2, 2, 471, 472, 3, 2, 2, 2, 472, 118, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 474, 478, 9, 37, 2, 2, 475, 477, 9, 38, 2, 2, 476, 475, 3, 2, 2, 2, 477, 480, 3, 2, 2, 2, 478, 476, 3, 2, 2, 2, 478, 479, 3, 2, 2, 2, 479, 120, 3, 2, 2, 2, 480, 478, 3, 2, 2, 2, 481, 482, 7, 40, 2, 2, 482, 122, 3, 2, 2, 2, 483, 484, 7, 40, 2, 2, 484, 485, 7, 40, 2, 2, 485, 124, 3, 2, 2, 2, 486, 487, 7, 40, 2, 2, 487, 488, 7, 62, 2, 2, 488, 126, 3, 2, 2, 2, 489, 490, 7, 66, 2, 2, 490, 491, 7, 66, 2, 2, 491, 128, 3, 2, 2, 2, 492, 493, 7, 66, 2, 2, 493, 494, 7, 64, 2, 2, 494, 130, 3, 2, 2, 2, 495, 496, 7, 66, 2, 2, 496, 132, 3, 2, 2, 2, 497, 498, 7, 35, 2, 2, 498, 134, 3, 2, 2, 2, 499, 500, 7, 35, 2, 2, 500, 501, 7, 35, 2, 2, 501, 136, 3, 2, 2, 2, 502, 503, 7, 35, 2, 2, 503, 504, 7, 63, 2, 2, 504, 138, 3, 2, 2, 2, 505, 506, 7, 96, 2, 2, 506, 140, 3, 2, 2, 2, 507, 508, 7, 63, 2, 2, 508, 142, 3, 2, 2, 2, 509, 510, 7, 63, 2, 2, 510, 511, 7, 64, 2, 2, 511, 144, 3, 2, 2, 2, 512, 513, 7, 64, 2, 2, 513, 146, 3, 2, 2, 2, 514, 515, 7, 64, 2, 2, 515, 516, 7, 63, 2, 2, 516, 148, 3, 2, 2, 2, 517, 518, 7, 64, 2, 2, 518, 519, 7, 64, 2, 2, 519, 150, 3, 2, 2, 2, 520, 521, 7, 37, 2, 2, 521, 152, 3, 2, 2, 2, 522, 523, 7, 37, 2, 2, 523, 524, 7, 63, 2, 2, 524, 154, 3, 2, 2, 2, 525, 526, 7, 37, 2, 2, 526, 527, 7, 64, 2, 2, 527, 156, 3, 2, 2, 2, 528, 529, 7, 37, 2, 2, 529, 530, 7, 64, 2, 2, 530, 531, 7, 64, 2, 2, 531, 158, 3, 2, 2, 2, 532, 533, 7, 37, 2, 2, 533, 534, 7, 37, 2, 2, 534, 160, 3, 2, 2, 2, 535, 536, 7, 47, 2, 2, 536, 537, 7, 64, 2, 2, 537, 162, 3, 2, 2, 2, 538, 539, 7, 47, 2, 2, 539, 540, 7, 64, 2, 2, 540, 541, 7, 64, 2, 2, 541, 164, 3, 2, 2, 2, 542, 543, 7, 47, 2, 2, 543, 544, 7, 126, 2, 2, 544, 545, 7, 47, 2, 2, 545, 166, 3, 2, 2, 2, 546, 547, 7, 62, 2, 2, 547, 168, 3, 2, 2, 2, 548, 549, 7, 62, 2, 2, 549, 550, 7, 63, 2, 2, 550, 170, 3, 2, 2, 2, 551, 552, 7, 62, 2, 2, 552, 553, 7, 66, 2, 2, 553, 172, 3, 2, 2, 2, 554, 555, 7, 62, 2, 2, 555, 556, 7, 96, 2, 2, 556, 174, 3, 2, 2, 2, 557, 558, 7, 62, 2, 2, 558, 559, 7, 64, 2, 2, 559, 176, 3, 2, 2, 2, 560, 561, 7, 62, 2, 2, 561, 562, 7, 47, 2, 2, 562, 563, 7, 64, 2, 2, 563, 178, 3, 2, 2, 2, 564, 565, 7, 62, 2, 2, 565, 566, 7, 62, 2, 2, 566, 180, 3, 2, 2, 2, 567, 568, 7, 62, 2, 2, 568, 569, 7, 62, 2, 2, 569, 570, 7, 63, 2, 2, 570, 182, 3, 2, 2, 2, 571, 572, 7, 62, 2, 2, 572, 573, 7, 65, 2, 2, 573, 574, 7, 64, 2, 2, 574, 184, 3, 2, 2, 2, 575, 576, 7, 47, 2, 2, 576, 186, 3, 2, 2, 2, 577, 578, 7, 39, 2, 2, 578, 188, 3, 2, 2, 2, 579, 580, 7, 126, 2, 2, 580, 190, 3, 2, 2, 2, 581, 582, 7, 126, 2, 2, 582, 583, 7, 126, 2, 2, 583, 192, 3, 2, 2, 2, 584, 585, 7, 126, 2, 2, 585, 586, 7, 126, 2, 2, 586, 587, 7, 49, 2, 2, 587, 194, 3, 2, 2, 2, 588, 589, 7, 126, 2, 2, 589, 590, 7, 49, 2, 2, 590, 196, 3, 2, 2, 2, 591, 592, 7, 45, 2, 2, 592, 198, 3, 2, 2, 2, 593, 594, 7, 65, 2, 2, 594, 200, 3, 2, 2, 2, 595, 596, 7, 65, 2, 2, 596, 597, 7, 40, 2, 2, 597, 202, 3, 2, 2, 2, 598, 599, 7, 65, 2, 2, 599, 600, 7, 37, 2, 2, 600, 204, 3, 2, 2, 2, 601, 602, 7, 65, 2, 2, 602, 603, 7, 47, 2, 2, 603, 206, 3, 2, 2, 2, 604, 605, 7, 65, 2, 2, 605, 606, 7, 126, 2, 2, 606, 208, 3, 2, 2, 2, 607, 608, 7, 49, 2, 2, 608, 210, 3, 2, 2, 2, 609, 610, 7, 128, 2, 2, 610, 212, 3, 2, 2, 2, 611, 612, 7, 128, 2, 2, 612, 613, 7, 63, 2, 2, 613, 214, 3, 2, 2, 2, 614, 615, 7, 128, 2, 2, 615, 616, 7, 64, 2, 2, 616, 617, 7, 63, 2, 2, 617, 618, 7, 128, 2, 2, 618, 216, 3, 2, 2, 2, 619, 620, 7, 128, 2, 2, 620, 621, 7, 64, 2, 2, 621, 622, 7, 128, 2, 2, 622, 218, 3, 2, 2, 2, 623, 624, 7, 128, 2, 2, 624, 625, 7, 62, 2, 2, 625, 626, 7, 63, 2, 2, 626, 627, 7, 128, 2, 2, 627, 220, 3, 2, 2, 2, 628, 629, 7, 128, 2, 2, 629, 630, 7, 62, 2, 2, 630, 631, 7, 128, 2, 2, 631, 222, 3, 2, 2, 2, 632, 633, 7, 128, 2, 2, 633, 634, 7, 44, 2, 2, 634, 224, 3, 2, 2, 2, 635, 636, 7, 128, 2, 2, 636, 637, 7, 128, 2, 2, 637, 226, 3, 2, 2, 2, 638, 639, 7, 61, 2, 2, 639, 228, 3, 2, 2, 2, 640, 641, 11, 2, 2, 2, 641, 230, 3, 2, 2, 2, 25, 2, 293, 295, 304, 306, 317, 319, 330, 342, 348, 406, 415, 420, 426, 432, 438, 440, 443, 448, 454, 456, 471, 478, 2] \ No newline at end of file diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.py b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.py index 7a4defd77..338c304f6 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.py +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.py @@ -5,17 +5,16 @@ from typing.io import TextIO import sys - def serializedATN(): with StringIO() as buf: buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2T") - buf.write("\u028a\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") + buf.write("\u0282\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36") - buf.write("\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%") - buf.write("\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") + buf.write('\t\36\4\37\t\37\4 \t \4!\t!\4"\t"\4#\t#\4$\t$\4%\t%') + buf.write("\4&\t&\4'\t'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") buf.write("\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64") buf.write("\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:") buf.write("\4;\t;\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\t") @@ -24,273 +23,270 @@ def serializedATN(): buf.write("U\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4") buf.write("^\t^\4_\t_\4`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4") buf.write("g\tg\4h\th\4i\ti\4j\tj\4k\tk\4l\tl\4m\tm\4n\tn\4o\to\4") - buf.write("p\tp\4q\tq\4r\tr\4s\ts\3\2\6\2\u00e9\n\2\r\2\16\2\u00ea") - buf.write("\3\2\3\2\3\3\3\3\3\3\3\3\7\3\u00f3\n\3\f\3\16\3\u00f6") - buf.write("\13\3\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\7\4\u0101\n") - buf.write("\4\f\4\16\4\u0104\13\4\3\4\3\4\3\4\3\4\3\5\3\5\3\6\3\6") - buf.write("\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r") - buf.write("\3\r\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\22\3\22") - buf.write("\3\23\3\23\3\24\3\24\3\25\3\25\3\26\3\26\3\27\3\27\3\30") - buf.write("\3\30\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\35\3\35") - buf.write("\3\36\3\36\3\37\3\37\3 \3 \3!\3!\3\"\3\"\3\"\3\"\7\"\u0148") - buf.write("\n\"\f\"\16\"\u014b\13\"\3\"\3\"\3#\3#\3#\3#\7#\u0153") - buf.write("\n#\f#\16#\u0156\13#\3#\3#\3$\3$\3$\3$\3$\3$\7$\u0160") - buf.write("\n$\f$\16$\u0163\13$\3$\3$\3%\3%\3%\3%\3%\3&\3&\3&\3&") - buf.write("\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3(\3") - buf.write("(\3(\3(\3(\3(\3)\3)\3*\3*\3+\3+\3+\3,\3,\3-\3-\3-\3.\3") - buf.write(".\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\63\3\63\3\63\7") - buf.write("\63\u019d\n\63\f\63\16\63\u01a0\13\63\3\63\3\63\3\64\3") - buf.write("\64\3\64\3\65\5\65\u01a8\n\65\3\65\6\65\u01ab\n\65\r\65") - buf.write("\16\65\u01ac\3\65\3\65\6\65\u01b1\n\65\r\65\16\65\u01b2") - buf.write("\3\65\3\65\7\65\u01b7\n\65\f\65\16\65\u01ba\13\65\3\65") - buf.write("\6\65\u01bd\n\65\r\65\16\65\u01be\5\65\u01c1\n\65\3\66") - buf.write("\5\66\u01c4\n\66\3\66\6\66\u01c7\n\66\r\66\16\66\u01c8") - buf.write("\3\66\3\66\6\66\u01cd\n\66\r\66\16\66\u01ce\5\66\u01d1") - buf.write("\n\66\3\67\3\67\3\67\38\38\39\39\3:\3:\3;\3;\7;\u01de") - buf.write("\n;\f;\16;\u01e1\13;\3<\3<\7<\u01e5\n<\f<\16<\u01e8\13") - buf.write("<\3=\3=\3>\3>\3>\3?\3?\3?\3@\3@\3@\3A\3A\3A\3B\3B\3C\3") - buf.write("C\3D\3D\3D\3E\3E\3E\3F\3F\3G\3G\3H\3H\3H\3I\3I\3J\3J\3") - buf.write("J\3K\3K\3K\3L\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3O\3P\3P\3") - buf.write("P\3Q\3Q\3Q\3R\3R\3R\3R\3S\3S\3S\3S\3T\3T\3U\3U\3U\3V\3") - buf.write("V\3V\3W\3W\3W\3X\3X\3X\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3[\3[\3[\3") - buf.write("[\3\\\3\\\3\\\3\\\3]\3]\3^\3^\3_\3_\3`\3`\3`\3a\3a\3a") - buf.write("\3a\3b\3b\3b\3c\3c\3d\3d\3e\3e\3e\3f\3f\3f\3g\3g\3g\3") - buf.write("h\3h\3h\3i\3i\3j\3j\3k\3k\3k\3l\3l\3l\3l\3l\3m\3m\3m\3") - buf.write("m\3n\3n\3n\3n\3n\3o\3o\3o\3o\3p\3p\3p\3q\3q\3q\3r\3r\3") - buf.write("s\3s\4\u00f4\u0102\2t\3\3\5\4\7\5\t\2\13\2\r\2\17\2\21") - buf.write("\2\23\2\25\2\27\2\31\2\33\2\35\2\37\2!\2#\2%\2\'\2)\2") - buf.write("+\2-\2/\2\61\2\63\2\65\2\67\29\2;\2=\2?\2A\2C\2E\2G\2") - buf.write("I\6K\7M\bO\tQ\nS\13U\fW\rY\16[\17]\20_\21a\22c\23e\24") - buf.write("g\25i\26k\27m\30o\31q\32s\33u\34w\35y\36{\37} \177!\u0081") - buf.write("\"\u0083#\u0085$\u0087%\u0089&\u008b\'\u008d(\u008f)\u0091") - buf.write("*\u0093+\u0095,\u0097-\u0099.\u009b/\u009d\60\u009f\61") - buf.write("\u00a1\62\u00a3\63\u00a5\64\u00a7\65\u00a9\66\u00ab\67") - buf.write("\u00ad8\u00af9\u00b1:\u00b3;\u00b5<\u00b7=\u00b9>\u00bb") - buf.write("?\u00bd@\u00bfA\u00c1B\u00c3C\u00c5D\u00c7E\u00c9F\u00cb") - buf.write("G\u00cdH\u00cfI\u00d1J\u00d3K\u00d5L\u00d7M\u00d9N\u00db") - buf.write("O\u00ddP\u00dfQ\u00e1R\u00e3S\u00e5T\3\2&\5\2\13\f\17") - buf.write("\17\"\"\4\2CCcc\4\2DDdd\4\2EEee\4\2FFff\4\2GGgg\4\2HH") - buf.write("hh\4\2IIii\4\2JJjj\4\2KKkk\4\2LLll\4\2MMmm\4\2NNnn\4\2") - buf.write("OOoo\4\2PPpp\4\2QQqq\4\2RRrr\4\2SSss\4\2TTtt\4\2UUuu\4") - buf.write("\2VVvv\4\2WWww\4\2XXxx\4\2YYyy\4\2ZZzz\4\2[[{{\4\2\\\\") - buf.write("||\4\2\62;CH\3\2\62;\4\2$$^^\4\2))^^\4\2^^bb\5\2C\\aa") - buf.write("c|\6\2\62;C\\aac|\6\2C\\aac|\u00a3\1\7\2\62;C\\aac|\u00a3") - buf.write("\1\2\u0280\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2I\3\2") - buf.write("\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3") - buf.write("\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]") - buf.write("\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2") - buf.write("g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2") - buf.write("\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2") - buf.write("\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2") - buf.write("\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089") - buf.write("\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2") - buf.write("\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097") - buf.write("\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2") - buf.write("\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5") - buf.write("\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2") - buf.write("\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3") - buf.write("\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2") - buf.write("\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1") - buf.write("\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2") - buf.write("\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf") - buf.write("\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2") - buf.write("\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd") - buf.write("\3\2\2\2\2\u00df\3\2\2\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2") - buf.write("\2\2\u00e5\3\2\2\2\3\u00e8\3\2\2\2\5\u00ee\3\2\2\2\7\u00fc") - buf.write("\3\2\2\2\t\u0109\3\2\2\2\13\u010b\3\2\2\2\r\u010d\3\2") - buf.write("\2\2\17\u010f\3\2\2\2\21\u0111\3\2\2\2\23\u0113\3\2\2") - buf.write("\2\25\u0115\3\2\2\2\27\u0117\3\2\2\2\31\u0119\3\2\2\2") - buf.write("\33\u011b\3\2\2\2\35\u011d\3\2\2\2\37\u011f\3\2\2\2!\u0121") - buf.write("\3\2\2\2#\u0123\3\2\2\2%\u0125\3\2\2\2\'\u0127\3\2\2\2") - buf.write(")\u0129\3\2\2\2+\u012b\3\2\2\2-\u012d\3\2\2\2/\u012f\3") - buf.write("\2\2\2\61\u0131\3\2\2\2\63\u0133\3\2\2\2\65\u0135\3\2") - buf.write("\2\2\67\u0137\3\2\2\29\u0139\3\2\2\2;\u013b\3\2\2\2=\u013d") - buf.write("\3\2\2\2?\u013f\3\2\2\2A\u0141\3\2\2\2C\u0143\3\2\2\2") - buf.write("E\u014e\3\2\2\2G\u0159\3\2\2\2I\u0166\3\2\2\2K\u016b\3") - buf.write("\2\2\2M\u0171\3\2\2\2O\u0177\3\2\2\2Q\u0183\3\2\2\2S\u0185") - buf.write("\3\2\2\2U\u0187\3\2\2\2W\u018a\3\2\2\2Y\u018c\3\2\2\2") - buf.write("[\u018f\3\2\2\2]\u0191\3\2\2\2_\u0193\3\2\2\2a\u0195\3") - buf.write("\2\2\2c\u0197\3\2\2\2e\u0199\3\2\2\2g\u01a3\3\2\2\2i\u01a7") - buf.write("\3\2\2\2k\u01c3\3\2\2\2m\u01d2\3\2\2\2o\u01d5\3\2\2\2") - buf.write("q\u01d7\3\2\2\2s\u01d9\3\2\2\2u\u01db\3\2\2\2w\u01e2\3") - buf.write("\2\2\2y\u01e9\3\2\2\2{\u01eb\3\2\2\2}\u01ee\3\2\2\2\177") - buf.write("\u01f1\3\2\2\2\u0081\u01f4\3\2\2\2\u0083\u01f7\3\2\2\2") - buf.write("\u0085\u01f9\3\2\2\2\u0087\u01fb\3\2\2\2\u0089\u01fe\3") - buf.write("\2\2\2\u008b\u0201\3\2\2\2\u008d\u0203\3\2\2\2\u008f\u0205") - buf.write("\3\2\2\2\u0091\u0208\3\2\2\2\u0093\u020a\3\2\2\2\u0095") - buf.write("\u020d\3\2\2\2\u0097\u0210\3\2\2\2\u0099\u0212\3\2\2\2") - buf.write("\u009b\u0215\3\2\2\2\u009d\u0218\3\2\2\2\u009f\u021c\3") - buf.write("\2\2\2\u00a1\u021f\3\2\2\2\u00a3\u0222\3\2\2\2\u00a5\u0226") - buf.write("\3\2\2\2\u00a7\u022a\3\2\2\2\u00a9\u022c\3\2\2\2\u00ab") - buf.write("\u022f\3\2\2\2\u00ad\u0232\3\2\2\2\u00af\u0235\3\2\2\2") - buf.write("\u00b1\u0238\3\2\2\2\u00b3\u023c\3\2\2\2\u00b5\u023f\3") - buf.write("\2\2\2\u00b7\u0243\3\2\2\2\u00b9\u0247\3\2\2\2\u00bb\u0249") - buf.write("\3\2\2\2\u00bd\u024b\3\2\2\2\u00bf\u024d\3\2\2\2\u00c1") - buf.write("\u0250\3\2\2\2\u00c3\u0254\3\2\2\2\u00c5\u0257\3\2\2\2") - buf.write("\u00c7\u0259\3\2\2\2\u00c9\u025b\3\2\2\2\u00cb\u025e\3") - buf.write("\2\2\2\u00cd\u0261\3\2\2\2\u00cf\u0264\3\2\2\2\u00d1\u0267") - buf.write("\3\2\2\2\u00d3\u0269\3\2\2\2\u00d5\u026b\3\2\2\2\u00d7") - buf.write("\u026e\3\2\2\2\u00d9\u0273\3\2\2\2\u00db\u0277\3\2\2\2") - buf.write("\u00dd\u027c\3\2\2\2\u00df\u0280\3\2\2\2\u00e1\u0283\3") - buf.write("\2\2\2\u00e3\u0286\3\2\2\2\u00e5\u0288\3\2\2\2\u00e7\u00e9") - buf.write("\t\2\2\2\u00e8\u00e7\3\2\2\2\u00e9\u00ea\3\2\2\2\u00ea") - buf.write("\u00e8\3\2\2\2\u00ea\u00eb\3\2\2\2\u00eb\u00ec\3\2\2\2") - buf.write("\u00ec\u00ed\b\2\2\2\u00ed\4\3\2\2\2\u00ee\u00ef\7\61") - buf.write("\2\2\u00ef\u00f0\7,\2\2\u00f0\u00f4\3\2\2\2\u00f1\u00f3") - buf.write("\13\2\2\2\u00f2\u00f1\3\2\2\2\u00f3\u00f6\3\2\2\2\u00f4") - buf.write("\u00f5\3\2\2\2\u00f4\u00f2\3\2\2\2\u00f5\u00f7\3\2\2\2") - buf.write("\u00f6\u00f4\3\2\2\2\u00f7\u00f8\7,\2\2\u00f8\u00f9\7") - buf.write("\61\2\2\u00f9\u00fa\3\2\2\2\u00fa\u00fb\b\3\2\2\u00fb") - buf.write("\6\3\2\2\2\u00fc\u00fd\7\61\2\2\u00fd\u00fe\7\61\2\2\u00fe") - buf.write("\u0102\3\2\2\2\u00ff\u0101\13\2\2\2\u0100\u00ff\3\2\2") - buf.write("\2\u0101\u0104\3\2\2\2\u0102\u0103\3\2\2\2\u0102\u0100") - buf.write("\3\2\2\2\u0103\u0105\3\2\2\2\u0104\u0102\3\2\2\2\u0105") - buf.write("\u0106\7\f\2\2\u0106\u0107\3\2\2\2\u0107\u0108\b\4\2\2") - buf.write("\u0108\b\3\2\2\2\u0109\u010a\t\3\2\2\u010a\n\3\2\2\2\u010b") - buf.write("\u010c\t\4\2\2\u010c\f\3\2\2\2\u010d\u010e\t\5\2\2\u010e") - buf.write("\16\3\2\2\2\u010f\u0110\t\6\2\2\u0110\20\3\2\2\2\u0111") - buf.write("\u0112\t\7\2\2\u0112\22\3\2\2\2\u0113\u0114\t\b\2\2\u0114") - buf.write("\24\3\2\2\2\u0115\u0116\t\t\2\2\u0116\26\3\2\2\2\u0117") - buf.write("\u0118\t\n\2\2\u0118\30\3\2\2\2\u0119\u011a\t\13\2\2\u011a") - buf.write("\32\3\2\2\2\u011b\u011c\t\f\2\2\u011c\34\3\2\2\2\u011d") - buf.write("\u011e\t\r\2\2\u011e\36\3\2\2\2\u011f\u0120\t\16\2\2\u0120") - buf.write(" \3\2\2\2\u0121\u0122\t\17\2\2\u0122\"\3\2\2\2\u0123\u0124") - buf.write("\t\20\2\2\u0124$\3\2\2\2\u0125\u0126\t\21\2\2\u0126&\3") - buf.write("\2\2\2\u0127\u0128\t\22\2\2\u0128(\3\2\2\2\u0129\u012a") - buf.write("\t\23\2\2\u012a*\3\2\2\2\u012b\u012c\t\24\2\2\u012c,\3") - buf.write("\2\2\2\u012d\u012e\t\25\2\2\u012e.\3\2\2\2\u012f\u0130") - buf.write("\t\26\2\2\u0130\60\3\2\2\2\u0131\u0132\t\27\2\2\u0132") - buf.write("\62\3\2\2\2\u0133\u0134\t\30\2\2\u0134\64\3\2\2\2\u0135") - buf.write("\u0136\t\31\2\2\u0136\66\3\2\2\2\u0137\u0138\t\32\2\2") - buf.write("\u01388\3\2\2\2\u0139\u013a\t\33\2\2\u013a:\3\2\2\2\u013b") - buf.write("\u013c\t\34\2\2\u013c<\3\2\2\2\u013d\u013e\7a\2\2\u013e") - buf.write(">\3\2\2\2\u013f\u0140\t\35\2\2\u0140@\3\2\2\2\u0141\u0142") - buf.write("\t\36\2\2\u0142B\3\2\2\2\u0143\u0149\7$\2\2\u0144\u0145") - buf.write("\7^\2\2\u0145\u0148\13\2\2\2\u0146\u0148\n\37\2\2\u0147") - buf.write("\u0144\3\2\2\2\u0147\u0146\3\2\2\2\u0148\u014b\3\2\2\2") - buf.write("\u0149\u0147\3\2\2\2\u0149\u014a\3\2\2\2\u014a\u014c\3") - buf.write("\2\2\2\u014b\u0149\3\2\2\2\u014c\u014d\7$\2\2\u014dD\3") - buf.write("\2\2\2\u014e\u0154\7)\2\2\u014f\u0150\7^\2\2\u0150\u0153") - buf.write("\13\2\2\2\u0151\u0153\n \2\2\u0152\u014f\3\2\2\2\u0152") - buf.write("\u0151\3\2\2\2\u0153\u0156\3\2\2\2\u0154\u0152\3\2\2\2") - buf.write("\u0154\u0155\3\2\2\2\u0155\u0157\3\2\2\2\u0156\u0154\3") - buf.write("\2\2\2\u0157\u0158\7)\2\2\u0158F\3\2\2\2\u0159\u0161\7") - buf.write("b\2\2\u015a\u015b\7^\2\2\u015b\u0160\13\2\2\2\u015c\u015d") - buf.write("\7b\2\2\u015d\u0160\7b\2\2\u015e\u0160\n!\2\2\u015f\u015a") - buf.write("\3\2\2\2\u015f\u015c\3\2\2\2\u015f\u015e\3\2\2\2\u0160") - buf.write("\u0163\3\2\2\2\u0161\u015f\3\2\2\2\u0161\u0162\3\2\2\2") - buf.write("\u0162\u0164\3\2\2\2\u0163\u0161\3\2\2\2\u0164\u0165\7") - buf.write("b\2\2\u0165H\3\2\2\2\u0166\u0167\5/\30\2\u0167\u0168\5") - buf.write("+\26\2\u0168\u0169\5\61\31\2\u0169\u016a\5\21\t\2\u016a") - buf.write("J\3\2\2\2\u016b\u016c\5\23\n\2\u016c\u016d\5\t\5\2\u016d") - buf.write("\u016e\5\37\20\2\u016e\u016f\5-\27\2\u016f\u0170\5\21") - buf.write("\t\2\u0170L\3\2\2\2\u0171\u0172\5\23\n\2\u0172\u0173\5") - buf.write("\31\r\2\u0173\u0174\5\21\t\2\u0174\u0175\5\37\20\2\u0175") - buf.write("\u0176\5\17\b\2\u0176N\3\2\2\2\u0177\u0178\5\23\n\2\u0178") - buf.write("\u0179\5\31\r\2\u0179\u017a\5\21\t\2\u017a\u017b\5\37") - buf.write("\20\2\u017b\u017c\5\17\b\2\u017c\u017d\5=\37\2\u017d\u017e") - buf.write("\5\13\6\2\u017e\u017f\59\35\2\u017f\u0180\5=\37\2\u0180") - buf.write("\u0181\5\31\r\2\u0181\u0182\5\17\b\2\u0182P\3\2\2\2\u0183") - buf.write("\u0184\7.\2\2\u0184R\3\2\2\2\u0185\u0186\7<\2\2\u0186") - buf.write("T\3\2\2\2\u0187\u0188\7<\2\2\u0188\u0189\7<\2\2\u0189") - buf.write("V\3\2\2\2\u018a\u018b\7&\2\2\u018bX\3\2\2\2\u018c\u018d") - buf.write("\7&\2\2\u018d\u018e\7&\2\2\u018eZ\3\2\2\2\u018f\u0190") - buf.write("\7,\2\2\u0190\\\3\2\2\2\u0191\u0192\7*\2\2\u0192^\3\2") - buf.write("\2\2\u0193\u0194\7+\2\2\u0194`\3\2\2\2\u0195\u0196\7]") - buf.write("\2\2\u0196b\3\2\2\2\u0197\u0198\7_\2\2\u0198d\3\2\2\2") - buf.write("\u0199\u019a\5\13\6\2\u019a\u019e\7)\2\2\u019b\u019d\4") - buf.write("\62\63\2\u019c\u019b\3\2\2\2\u019d\u01a0\3\2\2\2\u019e") - buf.write("\u019c\3\2\2\2\u019e\u019f\3\2\2\2\u019f\u01a1\3\2\2\2") - buf.write("\u01a0\u019e\3\2\2\2\u01a1\u01a2\7)\2\2\u01a2f\3\2\2\2") - buf.write("\u01a3\u01a4\5\21\t\2\u01a4\u01a5\5E#\2\u01a5h\3\2\2\2") - buf.write("\u01a6\u01a8\7/\2\2\u01a7\u01a6\3\2\2\2\u01a7\u01a8\3") - buf.write("\2\2\2\u01a8\u01aa\3\2\2\2\u01a9\u01ab\5A!\2\u01aa\u01a9") - buf.write("\3\2\2\2\u01ab\u01ac\3\2\2\2\u01ac\u01aa\3\2\2\2\u01ac") - buf.write("\u01ad\3\2\2\2\u01ad\u01ae\3\2\2\2\u01ae\u01b0\7\60\2") - buf.write("\2\u01af\u01b1\5A!\2\u01b0\u01af\3\2\2\2\u01b1\u01b2\3") - buf.write("\2\2\2\u01b2\u01b0\3\2\2\2\u01b2\u01b3\3\2\2\2\u01b3\u01c0") - buf.write("\3\2\2\2\u01b4\u01b8\5\21\t\2\u01b5\u01b7\7/\2\2\u01b6") - buf.write("\u01b5\3\2\2\2\u01b7\u01ba\3\2\2\2\u01b8\u01b6\3\2\2\2") - buf.write("\u01b8\u01b9\3\2\2\2\u01b9\u01bc\3\2\2\2\u01ba\u01b8\3") - buf.write("\2\2\2\u01bb\u01bd\5A!\2\u01bc\u01bb\3\2\2\2\u01bd\u01be") - buf.write("\3\2\2\2\u01be\u01bc\3\2\2\2\u01be\u01bf\3\2\2\2\u01bf") - buf.write("\u01c1\3\2\2\2\u01c0\u01b4\3\2\2\2\u01c0\u01c1\3\2\2\2") - buf.write("\u01c1j\3\2\2\2\u01c2\u01c4\7/\2\2\u01c3\u01c2\3\2\2\2") - buf.write("\u01c3\u01c4\3\2\2\2\u01c4\u01c6\3\2\2\2\u01c5\u01c7\5") - buf.write("A!\2\u01c6\u01c5\3\2\2\2\u01c7\u01c8\3\2\2\2\u01c8\u01c6") - buf.write("\3\2\2\2\u01c8\u01c9\3\2\2\2\u01c9\u01d0\3\2\2\2\u01ca") - buf.write("\u01cc\5\21\t\2\u01cb\u01cd\5A!\2\u01cc\u01cb\3\2\2\2") - buf.write("\u01cd\u01ce\3\2\2\2\u01ce\u01cc\3\2\2\2\u01ce\u01cf\3") - buf.write("\2\2\2\u01cf\u01d1\3\2\2\2\u01d0\u01ca\3\2\2\2\u01d0\u01d1") - buf.write("\3\2\2\2\u01d1l\3\2\2\2\u01d2\u01d3\7z\2\2\u01d3\u01d4") - buf.write("\5E#\2\u01d4n\3\2\2\2\u01d5\u01d6\7\60\2\2\u01d6p\3\2") - buf.write("\2\2\u01d7\u01d8\5E#\2\u01d8r\3\2\2\2\u01d9\u01da\5C\"") - buf.write("\2\u01dat\3\2\2\2\u01db\u01df\t\"\2\2\u01dc\u01de\t#\2") - buf.write("\2\u01dd\u01dc\3\2\2\2\u01de\u01e1\3\2\2\2\u01df\u01dd") - buf.write("\3\2\2\2\u01df\u01e0\3\2\2\2\u01e0v\3\2\2\2\u01e1\u01df") - buf.write("\3\2\2\2\u01e2\u01e6\t$\2\2\u01e3\u01e5\t%\2\2\u01e4\u01e3") - buf.write("\3\2\2\2\u01e5\u01e8\3\2\2\2\u01e6\u01e4\3\2\2\2\u01e6") - buf.write("\u01e7\3\2\2\2\u01e7x\3\2\2\2\u01e8\u01e6\3\2\2\2\u01e9") - buf.write("\u01ea\7(\2\2\u01eaz\3\2\2\2\u01eb\u01ec\7(\2\2\u01ec") - buf.write("\u01ed\7(\2\2\u01ed|\3\2\2\2\u01ee\u01ef\7(\2\2\u01ef") - buf.write("\u01f0\7>\2\2\u01f0~\3\2\2\2\u01f1\u01f2\7B\2\2\u01f2") - buf.write("\u01f3\7B\2\2\u01f3\u0080\3\2\2\2\u01f4\u01f5\7B\2\2\u01f5") - buf.write("\u01f6\7@\2\2\u01f6\u0082\3\2\2\2\u01f7\u01f8\7B\2\2\u01f8") - buf.write("\u0084\3\2\2\2\u01f9\u01fa\7#\2\2\u01fa\u0086\3\2\2\2") - buf.write("\u01fb\u01fc\7#\2\2\u01fc\u01fd\7#\2\2\u01fd\u0088\3\2") - buf.write("\2\2\u01fe\u01ff\7#\2\2\u01ff\u0200\7?\2\2\u0200\u008a") - buf.write("\3\2\2\2\u0201\u0202\7`\2\2\u0202\u008c\3\2\2\2\u0203") - buf.write("\u0204\7?\2\2\u0204\u008e\3\2\2\2\u0205\u0206\7?\2\2\u0206") - buf.write("\u0207\7@\2\2\u0207\u0090\3\2\2\2\u0208\u0209\7@\2\2\u0209") - buf.write("\u0092\3\2\2\2\u020a\u020b\7@\2\2\u020b\u020c\7?\2\2\u020c") - buf.write("\u0094\3\2\2\2\u020d\u020e\7@\2\2\u020e\u020f\7@\2\2\u020f") - buf.write("\u0096\3\2\2\2\u0210\u0211\7%\2\2\u0211\u0098\3\2\2\2") - buf.write("\u0212\u0213\7%\2\2\u0213\u0214\7?\2\2\u0214\u009a\3\2") - buf.write("\2\2\u0215\u0216\7%\2\2\u0216\u0217\7@\2\2\u0217\u009c") - buf.write("\3\2\2\2\u0218\u0219\7%\2\2\u0219\u021a\7@\2\2\u021a\u021b") - buf.write("\7@\2\2\u021b\u009e\3\2\2\2\u021c\u021d\7%\2\2\u021d\u021e") - buf.write("\7%\2\2\u021e\u00a0\3\2\2\2\u021f\u0220\7/\2\2\u0220\u0221") - buf.write("\7@\2\2\u0221\u00a2\3\2\2\2\u0222\u0223\7/\2\2\u0223\u0224") - buf.write("\7@\2\2\u0224\u0225\7@\2\2\u0225\u00a4\3\2\2\2\u0226\u0227") - buf.write("\7/\2\2\u0227\u0228\7~\2\2\u0228\u0229\7/\2\2\u0229\u00a6") - buf.write("\3\2\2\2\u022a\u022b\7>\2\2\u022b\u00a8\3\2\2\2\u022c") - buf.write("\u022d\7>\2\2\u022d\u022e\7?\2\2\u022e\u00aa\3\2\2\2\u022f") - buf.write("\u0230\7>\2\2\u0230\u0231\7B\2\2\u0231\u00ac\3\2\2\2\u0232") - buf.write("\u0233\7>\2\2\u0233\u0234\7`\2\2\u0234\u00ae\3\2\2\2\u0235") - buf.write("\u0236\7>\2\2\u0236\u0237\7@\2\2\u0237\u00b0\3\2\2\2\u0238") - buf.write("\u0239\7>\2\2\u0239\u023a\7/\2\2\u023a\u023b\7@\2\2\u023b") - buf.write("\u00b2\3\2\2\2\u023c\u023d\7>\2\2\u023d\u023e\7>\2\2\u023e") - buf.write("\u00b4\3\2\2\2\u023f\u0240\7>\2\2\u0240\u0241\7>\2\2\u0241") - buf.write("\u0242\7?\2\2\u0242\u00b6\3\2\2\2\u0243\u0244\7>\2\2\u0244") - buf.write("\u0245\7A\2\2\u0245\u0246\7@\2\2\u0246\u00b8\3\2\2\2\u0247") - buf.write("\u0248\7/\2\2\u0248\u00ba\3\2\2\2\u0249\u024a\7\'\2\2") - buf.write("\u024a\u00bc\3\2\2\2\u024b\u024c\7~\2\2\u024c\u00be\3") - buf.write("\2\2\2\u024d\u024e\7~\2\2\u024e\u024f\7~\2\2\u024f\u00c0") - buf.write("\3\2\2\2\u0250\u0251\7~\2\2\u0251\u0252\7~\2\2\u0252\u0253") - buf.write("\7\61\2\2\u0253\u00c2\3\2\2\2\u0254\u0255\7~\2\2\u0255") - buf.write("\u0256\7\61\2\2\u0256\u00c4\3\2\2\2\u0257\u0258\7-\2\2") - buf.write("\u0258\u00c6\3\2\2\2\u0259\u025a\7A\2\2\u025a\u00c8\3") - buf.write("\2\2\2\u025b\u025c\7A\2\2\u025c\u025d\7(\2\2\u025d\u00ca") - buf.write("\3\2\2\2\u025e\u025f\7A\2\2\u025f\u0260\7%\2\2\u0260\u00cc") - buf.write("\3\2\2\2\u0261\u0262\7A\2\2\u0262\u0263\7/\2\2\u0263\u00ce") - buf.write("\3\2\2\2\u0264\u0265\7A\2\2\u0265\u0266\7~\2\2\u0266\u00d0") - buf.write("\3\2\2\2\u0267\u0268\7\61\2\2\u0268\u00d2\3\2\2\2\u0269") - buf.write("\u026a\7\u0080\2\2\u026a\u00d4\3\2\2\2\u026b\u026c\7\u0080") - buf.write("\2\2\u026c\u026d\7?\2\2\u026d\u00d6\3\2\2\2\u026e\u026f") - buf.write("\7\u0080\2\2\u026f\u0270\7@\2\2\u0270\u0271\7?\2\2\u0271") - buf.write("\u0272\7\u0080\2\2\u0272\u00d8\3\2\2\2\u0273\u0274\7\u0080") - buf.write("\2\2\u0274\u0275\7@\2\2\u0275\u0276\7\u0080\2\2\u0276") - buf.write("\u00da\3\2\2\2\u0277\u0278\7\u0080\2\2\u0278\u0279\7>") - buf.write("\2\2\u0279\u027a\7?\2\2\u027a\u027b\7\u0080\2\2\u027b") - buf.write("\u00dc\3\2\2\2\u027c\u027d\7\u0080\2\2\u027d\u027e\7>") - buf.write("\2\2\u027e\u027f\7\u0080\2\2\u027f\u00de\3\2\2\2\u0280") - buf.write("\u0281\7\u0080\2\2\u0281\u0282\7,\2\2\u0282\u00e0\3\2") - buf.write("\2\2\u0283\u0284\7\u0080\2\2\u0284\u0285\7\u0080\2\2\u0285") - buf.write("\u00e2\3\2\2\2\u0286\u0287\7=\2\2\u0287\u00e4\3\2\2\2") - buf.write("\u0288\u0289\13\2\2\2\u0289\u00e6\3\2\2\2\31\2\u00ea\u00f4") - buf.write("\u0102\u0147\u0149\u0152\u0154\u015f\u0161\u019e\u01a7") - buf.write("\u01ac\u01b2\u01b8\u01be\u01c0\u01c3\u01c8\u01ce\u01d0") - buf.write("\u01df\u01e6\3\2\3\2") + buf.write("p\tp\4q\tq\4r\tr\4s\ts\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3\5") + buf.write("\3\6\3\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f") + buf.write("\3\f\3\r\3\r\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3") + buf.write("\22\3\22\3\23\3\23\3\24\3\24\3\25\3\25\3\26\3\26\3\27") + buf.write("\3\27\3\30\3\30\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34") + buf.write("\3\35\3\35\3\36\3\36\3\37\3\37\3\37\3\37\7\37\u0126\n") + buf.write("\37\f\37\16\37\u0129\13\37\3\37\3\37\3 \3 \3 \3 \7 \u0131") + buf.write("\n \f \16 \u0134\13 \3 \3 \3!\3!\3!\3!\3!\3!\7!\u013e") + buf.write('\n!\f!\16!\u0141\13!\3!\3!\3"\3"\3"\3"\7"\u0149\n') + buf.write('"\f"\16"\u014c\13"\3"\3"\3"\3#\3#\3#\3#\7#\u0155') + buf.write("\n#\f#\16#\u0158\13#\3$\6$\u015b\n$\r$\16$\u015c\3%\3") + buf.write("%\3%\3%\3%\3&\3&\3&\3&\3&\3&\3'\3'\3'\3'\3'\3'\3") + buf.write("(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3)\3)\3*\3*\3+\3+\3") + buf.write("+\3,\3,\3-\3-\3-\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\62") + buf.write("\3\62\3\63\3\63\3\63\7\63\u0195\n\63\f\63\16\63\u0198") + buf.write("\13\63\3\63\3\63\3\64\3\64\3\64\3\65\5\65\u01a0\n\65\3") + buf.write("\65\6\65\u01a3\n\65\r\65\16\65\u01a4\3\65\3\65\6\65\u01a9") + buf.write("\n\65\r\65\16\65\u01aa\3\65\3\65\7\65\u01af\n\65\f\65") + buf.write("\16\65\u01b2\13\65\3\65\6\65\u01b5\n\65\r\65\16\65\u01b6") + buf.write("\5\65\u01b9\n\65\3\66\5\66\u01bc\n\66\3\66\6\66\u01bf") + buf.write("\n\66\r\66\16\66\u01c0\3\66\3\66\6\66\u01c5\n\66\r\66") + buf.write("\16\66\u01c6\5\66\u01c9\n\66\3\67\3\67\3\67\38\38\39\3") + buf.write("9\3:\3:\3;\3;\7;\u01d6\n;\f;\16;\u01d9\13;\3<\3<\7<\u01dd") + buf.write("\n<\f<\16<\u01e0\13<\3=\3=\3>\3>\3>\3?\3?\3?\3@\3@\3@") + buf.write("\3A\3A\3A\3B\3B\3C\3C\3D\3D\3D\3E\3E\3E\3F\3F\3G\3G\3") + buf.write("H\3H\3H\3I\3I\3J\3J\3J\3K\3K\3K\3L\3L\3M\3M\3M\3N\3N\3") + buf.write("N\3O\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3R\3S\3S\3S\3") + buf.write("S\3T\3T\3U\3U\3U\3V\3V\3V\3W\3W\3W\3X\3X\3X\3Y\3Y\3Y\3") + buf.write("Y\3Z\3Z\3Z\3[\3[\3[\3[\3\\\3\\\3\\\3\\\3]\3]\3^\3^\3_") + buf.write("\3_\3`\3`\3`\3a\3a\3a\3a\3b\3b\3b\3c\3c\3d\3d\3e\3e\3") + buf.write("e\3f\3f\3f\3g\3g\3g\3h\3h\3h\3i\3i\3j\3j\3k\3k\3k\3l\3") + buf.write("l\3l\3l\3l\3m\3m\3m\3m\3n\3n\3n\3n\3n\3o\3o\3o\3o\3p\3") + buf.write("p\3p\3q\3q\3q\3r\3r\3s\3s\2\2t\3\2\5\2\7\2\t\2\13\2\r") + buf.write("\2\17\2\21\2\23\2\25\2\27\2\31\2\33\2\35\2\37\2!\2#\2") + buf.write("%\2'\2)\2+\2-\2/\2\61\2\63\2\65\2\67\29\2;\2=\2?\2A\2") + buf.write("C\3E\4G\5I\6K\7M\bO\tQ\nS\13U\fW\rY\16[\17]\20_\21a\22") + buf.write("c\23e\24g\25i\26k\27m\30o\31q\32s\33u\34w\35y\36{\37}") + buf.write(" \177!\u0081\"\u0083#\u0085$\u0087%\u0089&\u008b'\u008d") + buf.write("(\u008f)\u0091*\u0093+\u0095,\u0097-\u0099.\u009b/\u009d") + buf.write("\60\u009f\61\u00a1\62\u00a3\63\u00a5\64\u00a7\65\u00a9") + buf.write("\66\u00ab\67\u00ad8\u00af9\u00b1:\u00b3;\u00b5<\u00b7") + buf.write("=\u00b9>\u00bb?\u00bd@\u00bfA\u00c1B\u00c3C\u00c5D\u00c7") + buf.write("E\u00c9F\u00cbG\u00cdH\u00cfI\u00d1J\u00d3K\u00d5L\u00d7") + buf.write("M\u00d9N\u00dbO\u00ddP\u00dfQ\u00e1R\u00e3S\u00e5T\3\2") + buf.write("'\4\2CCcc\4\2DDdd\4\2EEee\4\2FFff\4\2GGgg\4\2HHhh\4\2") + buf.write("IIii\4\2JJjj\4\2KKkk\4\2LLll\4\2MMmm\4\2NNnn\4\2OOoo\4") + buf.write("\2PPpp\4\2QQqq\4\2RRrr\4\2SSss\4\2TTtt\4\2UUuu\4\2VVv") + buf.write("v\4\2WWww\4\2XXxx\4\2YYyy\4\2ZZzz\4\2[[{{\4\2\\\\||\4") + buf.write("\2\62;CH\3\2\62;\4\2$$^^\4\2))^^\4\2^^bb\4\2\f\f\17\17") + buf.write('\5\2\13\f\17\17""\5\2C\\aac|\6\2\62;C\\aac|\6\2C\\a') + buf.write("ac|\u00a3\1\7\2\62;C\\aac|\u00a3\1\2\u0278\2C\3\2\2\2") + buf.write("\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2") + buf.write("\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2") + buf.write("\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3") + buf.write("\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k") + buf.write("\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2") + buf.write("u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2") + buf.write("\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085") + buf.write("\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2") + buf.write("\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093") + buf.write("\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2") + buf.write("\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1") + buf.write("\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2") + buf.write("\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af") + buf.write("\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2") + buf.write("\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd") + buf.write("\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2") + buf.write("\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb") + buf.write("\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2") + buf.write("\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9") + buf.write("\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2") + buf.write("\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\3\u00e7") + buf.write("\3\2\2\2\5\u00e9\3\2\2\2\7\u00eb\3\2\2\2\t\u00ed\3\2\2") + buf.write("\2\13\u00ef\3\2\2\2\r\u00f1\3\2\2\2\17\u00f3\3\2\2\2\21") + buf.write("\u00f5\3\2\2\2\23\u00f7\3\2\2\2\25\u00f9\3\2\2\2\27\u00fb") + buf.write("\3\2\2\2\31\u00fd\3\2\2\2\33\u00ff\3\2\2\2\35\u0101\3") + buf.write("\2\2\2\37\u0103\3\2\2\2!\u0105\3\2\2\2#\u0107\3\2\2\2") + buf.write("%\u0109\3\2\2\2'\u010b\3\2\2\2)\u010d\3\2\2\2+\u010f") + buf.write("\3\2\2\2-\u0111\3\2\2\2/\u0113\3\2\2\2\61\u0115\3\2\2") + buf.write("\2\63\u0117\3\2\2\2\65\u0119\3\2\2\2\67\u011b\3\2\2\2") + buf.write("9\u011d\3\2\2\2;\u011f\3\2\2\2=\u0121\3\2\2\2?\u012c\3") + buf.write("\2\2\2A\u0137\3\2\2\2C\u0144\3\2\2\2E\u0150\3\2\2\2G\u015a") + buf.write("\3\2\2\2I\u015e\3\2\2\2K\u0163\3\2\2\2M\u0169\3\2\2\2") + buf.write("O\u016f\3\2\2\2Q\u017b\3\2\2\2S\u017d\3\2\2\2U\u017f\3") + buf.write("\2\2\2W\u0182\3\2\2\2Y\u0184\3\2\2\2[\u0187\3\2\2\2]\u0189") + buf.write("\3\2\2\2_\u018b\3\2\2\2a\u018d\3\2\2\2c\u018f\3\2\2\2") + buf.write("e\u0191\3\2\2\2g\u019b\3\2\2\2i\u019f\3\2\2\2k\u01bb\3") + buf.write("\2\2\2m\u01ca\3\2\2\2o\u01cd\3\2\2\2q\u01cf\3\2\2\2s\u01d1") + buf.write("\3\2\2\2u\u01d3\3\2\2\2w\u01da\3\2\2\2y\u01e1\3\2\2\2") + buf.write("{\u01e3\3\2\2\2}\u01e6\3\2\2\2\177\u01e9\3\2\2\2\u0081") + buf.write("\u01ec\3\2\2\2\u0083\u01ef\3\2\2\2\u0085\u01f1\3\2\2\2") + buf.write("\u0087\u01f3\3\2\2\2\u0089\u01f6\3\2\2\2\u008b\u01f9\3") + buf.write("\2\2\2\u008d\u01fb\3\2\2\2\u008f\u01fd\3\2\2\2\u0091\u0200") + buf.write("\3\2\2\2\u0093\u0202\3\2\2\2\u0095\u0205\3\2\2\2\u0097") + buf.write("\u0208\3\2\2\2\u0099\u020a\3\2\2\2\u009b\u020d\3\2\2\2") + buf.write("\u009d\u0210\3\2\2\2\u009f\u0214\3\2\2\2\u00a1\u0217\3") + buf.write("\2\2\2\u00a3\u021a\3\2\2\2\u00a5\u021e\3\2\2\2\u00a7\u0222") + buf.write("\3\2\2\2\u00a9\u0224\3\2\2\2\u00ab\u0227\3\2\2\2\u00ad") + buf.write("\u022a\3\2\2\2\u00af\u022d\3\2\2\2\u00b1\u0230\3\2\2\2") + buf.write("\u00b3\u0234\3\2\2\2\u00b5\u0237\3\2\2\2\u00b7\u023b\3") + buf.write("\2\2\2\u00b9\u023f\3\2\2\2\u00bb\u0241\3\2\2\2\u00bd\u0243") + buf.write("\3\2\2\2\u00bf\u0245\3\2\2\2\u00c1\u0248\3\2\2\2\u00c3") + buf.write("\u024c\3\2\2\2\u00c5\u024f\3\2\2\2\u00c7\u0251\3\2\2\2") + buf.write("\u00c9\u0253\3\2\2\2\u00cb\u0256\3\2\2\2\u00cd\u0259\3") + buf.write("\2\2\2\u00cf\u025c\3\2\2\2\u00d1\u025f\3\2\2\2\u00d3\u0261") + buf.write("\3\2\2\2\u00d5\u0263\3\2\2\2\u00d7\u0266\3\2\2\2\u00d9") + buf.write("\u026b\3\2\2\2\u00db\u026f\3\2\2\2\u00dd\u0274\3\2\2\2") + buf.write("\u00df\u0278\3\2\2\2\u00e1\u027b\3\2\2\2\u00e3\u027e\3") + buf.write("\2\2\2\u00e5\u0280\3\2\2\2\u00e7\u00e8\t\2\2\2\u00e8\4") + buf.write("\3\2\2\2\u00e9\u00ea\t\3\2\2\u00ea\6\3\2\2\2\u00eb\u00ec") + buf.write("\t\4\2\2\u00ec\b\3\2\2\2\u00ed\u00ee\t\5\2\2\u00ee\n\3") + buf.write("\2\2\2\u00ef\u00f0\t\6\2\2\u00f0\f\3\2\2\2\u00f1\u00f2") + buf.write("\t\7\2\2\u00f2\16\3\2\2\2\u00f3\u00f4\t\b\2\2\u00f4\20") + buf.write("\3\2\2\2\u00f5\u00f6\t\t\2\2\u00f6\22\3\2\2\2\u00f7\u00f8") + buf.write("\t\n\2\2\u00f8\24\3\2\2\2\u00f9\u00fa\t\13\2\2\u00fa\26") + buf.write("\3\2\2\2\u00fb\u00fc\t\f\2\2\u00fc\30\3\2\2\2\u00fd\u00fe") + buf.write("\t\r\2\2\u00fe\32\3\2\2\2\u00ff\u0100\t\16\2\2\u0100\34") + buf.write("\3\2\2\2\u0101\u0102\t\17\2\2\u0102\36\3\2\2\2\u0103\u0104") + buf.write('\t\20\2\2\u0104 \3\2\2\2\u0105\u0106\t\21\2\2\u0106"') + buf.write("\3\2\2\2\u0107\u0108\t\22\2\2\u0108$\3\2\2\2\u0109\u010a") + buf.write("\t\23\2\2\u010a&\3\2\2\2\u010b\u010c\t\24\2\2\u010c(\3") + buf.write("\2\2\2\u010d\u010e\t\25\2\2\u010e*\3\2\2\2\u010f\u0110") + buf.write("\t\26\2\2\u0110,\3\2\2\2\u0111\u0112\t\27\2\2\u0112.\3") + buf.write("\2\2\2\u0113\u0114\t\30\2\2\u0114\60\3\2\2\2\u0115\u0116") + buf.write("\t\31\2\2\u0116\62\3\2\2\2\u0117\u0118\t\32\2\2\u0118") + buf.write("\64\3\2\2\2\u0119\u011a\t\33\2\2\u011a\66\3\2\2\2\u011b") + buf.write("\u011c\7a\2\2\u011c8\3\2\2\2\u011d\u011e\t\34\2\2\u011e") + buf.write(":\3\2\2\2\u011f\u0120\t\35\2\2\u0120<\3\2\2\2\u0121\u0127") + buf.write("\7$\2\2\u0122\u0123\7^\2\2\u0123\u0126\13\2\2\2\u0124") + buf.write("\u0126\n\36\2\2\u0125\u0122\3\2\2\2\u0125\u0124\3\2\2") + buf.write("\2\u0126\u0129\3\2\2\2\u0127\u0125\3\2\2\2\u0127\u0128") + buf.write("\3\2\2\2\u0128\u012a\3\2\2\2\u0129\u0127\3\2\2\2\u012a") + buf.write("\u012b\7$\2\2\u012b>\3\2\2\2\u012c\u0132\7)\2\2\u012d") + buf.write("\u012e\7^\2\2\u012e\u0131\13\2\2\2\u012f\u0131\n\37\2") + buf.write("\2\u0130\u012d\3\2\2\2\u0130\u012f\3\2\2\2\u0131\u0134") + buf.write("\3\2\2\2\u0132\u0130\3\2\2\2\u0132\u0133\3\2\2\2\u0133") + buf.write("\u0135\3\2\2\2\u0134\u0132\3\2\2\2\u0135\u0136\7)\2\2") + buf.write("\u0136@\3\2\2\2\u0137\u013f\7b\2\2\u0138\u0139\7^\2\2") + buf.write("\u0139\u013e\13\2\2\2\u013a\u013b\7b\2\2\u013b\u013e\7") + buf.write("b\2\2\u013c\u013e\n \2\2\u013d\u0138\3\2\2\2\u013d\u013a") + buf.write("\3\2\2\2\u013d\u013c\3\2\2\2\u013e\u0141\3\2\2\2\u013f") + buf.write("\u013d\3\2\2\2\u013f\u0140\3\2\2\2\u0140\u0142\3\2\2\2") + buf.write("\u0141\u013f\3\2\2\2\u0142\u0143\7b\2\2\u0143B\3\2\2\2") + buf.write("\u0144\u0145\7\61\2\2\u0145\u0146\7,\2\2\u0146\u014a\3") + buf.write("\2\2\2\u0147\u0149\13\2\2\2\u0148\u0147\3\2\2\2\u0149") + buf.write("\u014c\3\2\2\2\u014a\u0148\3\2\2\2\u014a\u014b\3\2\2\2") + buf.write("\u014b\u014d\3\2\2\2\u014c\u014a\3\2\2\2\u014d\u014e\7") + buf.write(",\2\2\u014e\u014f\7\61\2\2\u014fD\3\2\2\2\u0150\u0151") + buf.write("\7\61\2\2\u0151\u0152\7\61\2\2\u0152\u0156\3\2\2\2\u0153") + buf.write("\u0155\n!\2\2\u0154\u0153\3\2\2\2\u0155\u0158\3\2\2\2") + buf.write("\u0156\u0154\3\2\2\2\u0156\u0157\3\2\2\2\u0157F\3\2\2") + buf.write('\2\u0158\u0156\3\2\2\2\u0159\u015b\t"\2\2\u015a\u0159') + buf.write("\3\2\2\2\u015b\u015c\3\2\2\2\u015c\u015a\3\2\2\2\u015c") + buf.write("\u015d\3\2\2\2\u015dH\3\2\2\2\u015e\u015f\5)\25\2\u015f") + buf.write("\u0160\5%\23\2\u0160\u0161\5+\26\2\u0161\u0162\5\13\6") + buf.write("\2\u0162J\3\2\2\2\u0163\u0164\5\r\7\2\u0164\u0165\5\3") + buf.write("\2\2\u0165\u0166\5\31\r\2\u0166\u0167\5'\24\2\u0167\u0168") + buf.write("\5\13\6\2\u0168L\3\2\2\2\u0169\u016a\5\r\7\2\u016a\u016b") + buf.write("\5\23\n\2\u016b\u016c\5\13\6\2\u016c\u016d\5\31\r\2\u016d") + buf.write("\u016e\5\t\5\2\u016eN\3\2\2\2\u016f\u0170\5\r\7\2\u0170") + buf.write("\u0171\5\23\n\2\u0171\u0172\5\13\6\2\u0172\u0173\5\31") + buf.write("\r\2\u0173\u0174\5\t\5\2\u0174\u0175\5\67\34\2\u0175\u0176") + buf.write("\5\5\3\2\u0176\u0177\5\63\32\2\u0177\u0178\5\67\34\2\u0178") + buf.write("\u0179\5\23\n\2\u0179\u017a\5\t\5\2\u017aP\3\2\2\2\u017b") + buf.write("\u017c\7.\2\2\u017cR\3\2\2\2\u017d\u017e\7<\2\2\u017e") + buf.write("T\3\2\2\2\u017f\u0180\7<\2\2\u0180\u0181\7<\2\2\u0181") + buf.write("V\3\2\2\2\u0182\u0183\7&\2\2\u0183X\3\2\2\2\u0184\u0185") + buf.write("\7&\2\2\u0185\u0186\7&\2\2\u0186Z\3\2\2\2\u0187\u0188") + buf.write("\7,\2\2\u0188\\\3\2\2\2\u0189\u018a\7*\2\2\u018a^\3\2") + buf.write("\2\2\u018b\u018c\7+\2\2\u018c`\3\2\2\2\u018d\u018e\7]") + buf.write("\2\2\u018eb\3\2\2\2\u018f\u0190\7_\2\2\u0190d\3\2\2\2") + buf.write("\u0191\u0192\5\5\3\2\u0192\u0196\7)\2\2\u0193\u0195\4") + buf.write("\62\63\2\u0194\u0193\3\2\2\2\u0195\u0198\3\2\2\2\u0196") + buf.write("\u0194\3\2\2\2\u0196\u0197\3\2\2\2\u0197\u0199\3\2\2\2") + buf.write("\u0198\u0196\3\2\2\2\u0199\u019a\7)\2\2\u019af\3\2\2\2") + buf.write("\u019b\u019c\5\13\6\2\u019c\u019d\5? \2\u019dh\3\2\2\2") + buf.write("\u019e\u01a0\7/\2\2\u019f\u019e\3\2\2\2\u019f\u01a0\3") + buf.write("\2\2\2\u01a0\u01a2\3\2\2\2\u01a1\u01a3\5;\36\2\u01a2\u01a1") + buf.write("\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a2\3\2\2\2\u01a4") + buf.write("\u01a5\3\2\2\2\u01a5\u01a6\3\2\2\2\u01a6\u01a8\7\60\2") + buf.write("\2\u01a7\u01a9\5;\36\2\u01a8\u01a7\3\2\2\2\u01a9\u01aa") + buf.write("\3\2\2\2\u01aa\u01a8\3\2\2\2\u01aa\u01ab\3\2\2\2\u01ab") + buf.write("\u01b8\3\2\2\2\u01ac\u01b0\5\13\6\2\u01ad\u01af\7/\2\2") + buf.write("\u01ae\u01ad\3\2\2\2\u01af\u01b2\3\2\2\2\u01b0\u01ae\3") + buf.write("\2\2\2\u01b0\u01b1\3\2\2\2\u01b1\u01b4\3\2\2\2\u01b2\u01b0") + buf.write("\3\2\2\2\u01b3\u01b5\5;\36\2\u01b4\u01b3\3\2\2\2\u01b5") + buf.write("\u01b6\3\2\2\2\u01b6\u01b4\3\2\2\2\u01b6\u01b7\3\2\2\2") + buf.write("\u01b7\u01b9\3\2\2\2\u01b8\u01ac\3\2\2\2\u01b8\u01b9\3") + buf.write("\2\2\2\u01b9j\3\2\2\2\u01ba\u01bc\7/\2\2\u01bb\u01ba\3") + buf.write("\2\2\2\u01bb\u01bc\3\2\2\2\u01bc\u01be\3\2\2\2\u01bd\u01bf") + buf.write("\5;\36\2\u01be\u01bd\3\2\2\2\u01bf\u01c0\3\2\2\2\u01c0") + buf.write("\u01be\3\2\2\2\u01c0\u01c1\3\2\2\2\u01c1\u01c8\3\2\2\2") + buf.write("\u01c2\u01c4\5\13\6\2\u01c3\u01c5\5;\36\2\u01c4\u01c3") + buf.write("\3\2\2\2\u01c5\u01c6\3\2\2\2\u01c6\u01c4\3\2\2\2\u01c6") + buf.write("\u01c7\3\2\2\2\u01c7\u01c9\3\2\2\2\u01c8\u01c2\3\2\2\2") + buf.write("\u01c8\u01c9\3\2\2\2\u01c9l\3\2\2\2\u01ca\u01cb\7z\2\2") + buf.write("\u01cb\u01cc\5? \2\u01ccn\3\2\2\2\u01cd\u01ce\7\60\2\2") + buf.write("\u01cep\3\2\2\2\u01cf\u01d0\5? \2\u01d0r\3\2\2\2\u01d1") + buf.write("\u01d2\5=\37\2\u01d2t\3\2\2\2\u01d3\u01d7\t#\2\2\u01d4") + buf.write("\u01d6\t$\2\2\u01d5\u01d4\3\2\2\2\u01d6\u01d9\3\2\2\2") + buf.write("\u01d7\u01d5\3\2\2\2\u01d7\u01d8\3\2\2\2\u01d8v\3\2\2") + buf.write("\2\u01d9\u01d7\3\2\2\2\u01da\u01de\t%\2\2\u01db\u01dd") + buf.write("\t&\2\2\u01dc\u01db\3\2\2\2\u01dd\u01e0\3\2\2\2\u01de") + buf.write("\u01dc\3\2\2\2\u01de\u01df\3\2\2\2\u01dfx\3\2\2\2\u01e0") + buf.write("\u01de\3\2\2\2\u01e1\u01e2\7(\2\2\u01e2z\3\2\2\2\u01e3") + buf.write("\u01e4\7(\2\2\u01e4\u01e5\7(\2\2\u01e5|\3\2\2\2\u01e6") + buf.write("\u01e7\7(\2\2\u01e7\u01e8\7>\2\2\u01e8~\3\2\2\2\u01e9") + buf.write("\u01ea\7B\2\2\u01ea\u01eb\7B\2\2\u01eb\u0080\3\2\2\2\u01ec") + buf.write("\u01ed\7B\2\2\u01ed\u01ee\7@\2\2\u01ee\u0082\3\2\2\2\u01ef") + buf.write("\u01f0\7B\2\2\u01f0\u0084\3\2\2\2\u01f1\u01f2\7#\2\2\u01f2") + buf.write("\u0086\3\2\2\2\u01f3\u01f4\7#\2\2\u01f4\u01f5\7#\2\2\u01f5") + buf.write("\u0088\3\2\2\2\u01f6\u01f7\7#\2\2\u01f7\u01f8\7?\2\2\u01f8") + buf.write("\u008a\3\2\2\2\u01f9\u01fa\7`\2\2\u01fa\u008c\3\2\2\2") + buf.write("\u01fb\u01fc\7?\2\2\u01fc\u008e\3\2\2\2\u01fd\u01fe\7") + buf.write("?\2\2\u01fe\u01ff\7@\2\2\u01ff\u0090\3\2\2\2\u0200\u0201") + buf.write("\7@\2\2\u0201\u0092\3\2\2\2\u0202\u0203\7@\2\2\u0203\u0204") + buf.write("\7?\2\2\u0204\u0094\3\2\2\2\u0205\u0206\7@\2\2\u0206\u0207") + buf.write("\7@\2\2\u0207\u0096\3\2\2\2\u0208\u0209\7%\2\2\u0209\u0098") + buf.write("\3\2\2\2\u020a\u020b\7%\2\2\u020b\u020c\7?\2\2\u020c\u009a") + buf.write("\3\2\2\2\u020d\u020e\7%\2\2\u020e\u020f\7@\2\2\u020f\u009c") + buf.write("\3\2\2\2\u0210\u0211\7%\2\2\u0211\u0212\7@\2\2\u0212\u0213") + buf.write("\7@\2\2\u0213\u009e\3\2\2\2\u0214\u0215\7%\2\2\u0215\u0216") + buf.write("\7%\2\2\u0216\u00a0\3\2\2\2\u0217\u0218\7/\2\2\u0218\u0219") + buf.write("\7@\2\2\u0219\u00a2\3\2\2\2\u021a\u021b\7/\2\2\u021b\u021c") + buf.write("\7@\2\2\u021c\u021d\7@\2\2\u021d\u00a4\3\2\2\2\u021e\u021f") + buf.write("\7/\2\2\u021f\u0220\7~\2\2\u0220\u0221\7/\2\2\u0221\u00a6") + buf.write("\3\2\2\2\u0222\u0223\7>\2\2\u0223\u00a8\3\2\2\2\u0224") + buf.write("\u0225\7>\2\2\u0225\u0226\7?\2\2\u0226\u00aa\3\2\2\2\u0227") + buf.write("\u0228\7>\2\2\u0228\u0229\7B\2\2\u0229\u00ac\3\2\2\2\u022a") + buf.write("\u022b\7>\2\2\u022b\u022c\7`\2\2\u022c\u00ae\3\2\2\2\u022d") + buf.write("\u022e\7>\2\2\u022e\u022f\7@\2\2\u022f\u00b0\3\2\2\2\u0230") + buf.write("\u0231\7>\2\2\u0231\u0232\7/\2\2\u0232\u0233\7@\2\2\u0233") + buf.write("\u00b2\3\2\2\2\u0234\u0235\7>\2\2\u0235\u0236\7>\2\2\u0236") + buf.write("\u00b4\3\2\2\2\u0237\u0238\7>\2\2\u0238\u0239\7>\2\2\u0239") + buf.write("\u023a\7?\2\2\u023a\u00b6\3\2\2\2\u023b\u023c\7>\2\2\u023c") + buf.write("\u023d\7A\2\2\u023d\u023e\7@\2\2\u023e\u00b8\3\2\2\2\u023f") + buf.write("\u0240\7/\2\2\u0240\u00ba\3\2\2\2\u0241\u0242\7'\2\2") + buf.write("\u0242\u00bc\3\2\2\2\u0243\u0244\7~\2\2\u0244\u00be\3") + buf.write("\2\2\2\u0245\u0246\7~\2\2\u0246\u0247\7~\2\2\u0247\u00c0") + buf.write("\3\2\2\2\u0248\u0249\7~\2\2\u0249\u024a\7~\2\2\u024a\u024b") + buf.write("\7\61\2\2\u024b\u00c2\3\2\2\2\u024c\u024d\7~\2\2\u024d") + buf.write("\u024e\7\61\2\2\u024e\u00c4\3\2\2\2\u024f\u0250\7-\2\2") + buf.write("\u0250\u00c6\3\2\2\2\u0251\u0252\7A\2\2\u0252\u00c8\3") + buf.write("\2\2\2\u0253\u0254\7A\2\2\u0254\u0255\7(\2\2\u0255\u00ca") + buf.write("\3\2\2\2\u0256\u0257\7A\2\2\u0257\u0258\7%\2\2\u0258\u00cc") + buf.write("\3\2\2\2\u0259\u025a\7A\2\2\u025a\u025b\7/\2\2\u025b\u00ce") + buf.write("\3\2\2\2\u025c\u025d\7A\2\2\u025d\u025e\7~\2\2\u025e\u00d0") + buf.write("\3\2\2\2\u025f\u0260\7\61\2\2\u0260\u00d2\3\2\2\2\u0261") + buf.write("\u0262\7\u0080\2\2\u0262\u00d4\3\2\2\2\u0263\u0264\7\u0080") + buf.write("\2\2\u0264\u0265\7?\2\2\u0265\u00d6\3\2\2\2\u0266\u0267") + buf.write("\7\u0080\2\2\u0267\u0268\7@\2\2\u0268\u0269\7?\2\2\u0269") + buf.write("\u026a\7\u0080\2\2\u026a\u00d8\3\2\2\2\u026b\u026c\7\u0080") + buf.write("\2\2\u026c\u026d\7@\2\2\u026d\u026e\7\u0080\2\2\u026e") + buf.write("\u00da\3\2\2\2\u026f\u0270\7\u0080\2\2\u0270\u0271\7>") + buf.write("\2\2\u0271\u0272\7?\2\2\u0272\u0273\7\u0080\2\2\u0273") + buf.write("\u00dc\3\2\2\2\u0274\u0275\7\u0080\2\2\u0275\u0276\7>") + buf.write("\2\2\u0276\u0277\7\u0080\2\2\u0277\u00de\3\2\2\2\u0278") + buf.write("\u0279\7\u0080\2\2\u0279\u027a\7,\2\2\u027a\u00e0\3\2") + buf.write("\2\2\u027b\u027c\7\u0080\2\2\u027c\u027d\7\u0080\2\2\u027d") + buf.write("\u00e2\3\2\2\2\u027e\u027f\7=\2\2\u027f\u00e4\3\2\2\2") + buf.write("\u0280\u0281\13\2\2\2\u0281\u00e6\3\2\2\2\31\2\u0125\u0127") + buf.write("\u0130\u0132\u013d\u013f\u014a\u0156\u015c\u0196\u019f") + buf.write("\u01a4\u01aa\u01b0\u01b6\u01b8\u01bb\u01c0\u01c6\u01c8") + buf.write("\u01d7\u01de\2") return buf.getvalue() @@ -298,11 +294,11 @@ class BaserowFormulaLexer(Lexer): atn = ATNDeserializer().deserialize(serializedATN()) - decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] - WHITESPACE = 1 - BLOCK_COMMENT = 2 - LINE_COMMENT = 3 + BLOCK_COMMENT = 1 + LINE_COMMENT = 2 + WHITESPACE = 3 TRUE = 4 FALSE = 5 FIELD = 6 @@ -383,69 +379,289 @@ class BaserowFormulaLexer(Lexer): SEMI = 81 ErrorCharacter = 82 - channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] + channelNames = [u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN"] - modeNames = [ "DEFAULT_MODE" ] + modeNames = ["DEFAULT_MODE"] - literalNames = [ "<INVALID>", - "','", "':'", "'::'", "'$'", "'$$'", "'*'", "'('", "')'", "'['", - "']'", "'.'", "'&'", "'&&'", "'&<'", "'@@'", "'@>'", "'@'", - "'!'", "'!!'", "'!='", "'^'", "'='", "'=>'", "'>'", "'>='", - "'>>'", "'#'", "'#='", "'#>'", "'#>>'", "'##'", "'->'", "'->>'", - "'-|-'", "'<'", "'<='", "'<@'", "'<^'", "'<>'", "'<->'", "'<<'", - "'<<='", "'<?>'", "'-'", "'%'", "'|'", "'||'", "'||/'", "'|/'", - "'+'", "'?'", "'?&'", "'?#'", "'?-'", "'?|'", "'/'", "'~'", - "'~='", "'~>=~'", "'~>~'", "'~<=~'", "'~<~'", "'~*'", "'~~'", - "';'" ] + literalNames = [ + "<INVALID>", + "','", + "':'", + "'::'", + "'$'", + "'$$'", + "'*'", + "'('", + "')'", + "'['", + "']'", + "'.'", + "'&'", + "'&&'", + "'&<'", + "'@@'", + "'@>'", + "'@'", + "'!'", + "'!!'", + "'!='", + "'^'", + "'='", + "'=>'", + "'>'", + "'>='", + "'>>'", + "'#'", + "'#='", + "'#>'", + "'#>>'", + "'##'", + "'->'", + "'->>'", + "'-|-'", + "'<'", + "'<='", + "'<@'", + "'<^'", + "'<>'", + "'<->'", + "'<<'", + "'<<='", + "'<?>'", + "'-'", + "'%'", + "'|'", + "'||'", + "'||/'", + "'|/'", + "'+'", + "'?'", + "'?&'", + "'?#'", + "'?-'", + "'?|'", + "'/'", + "'~'", + "'~='", + "'~>=~'", + "'~>~'", + "'~<=~'", + "'~<~'", + "'~*'", + "'~~'", + "';'", + ] - symbolicNames = [ "<INVALID>", - "WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", "TRUE", "FALSE", - "FIELD", "FIELDBYID", "COMMA", "COLON", "COLON_COLON", "DOLLAR", - "DOLLAR_DOLLAR", "STAR", "OPEN_PAREN", "CLOSE_PAREN", "OPEN_BRACKET", - "CLOSE_BRACKET", "BIT_STRING", "REGEX_STRING", "NUMERIC_LITERAL", - "INTEGER_LITERAL", "HEX_INTEGER_LITERAL", "DOT", "SINGLEQ_STRING_LITERAL", - "DOUBLEQ_STRING_LITERAL", "IDENTIFIER", "IDENTIFIER_UNICODE", - "AMP", "AMP_AMP", "AMP_LT", "AT_AT", "AT_GT", "AT_SIGN", "BANG", - "BANG_BANG", "BANG_EQUAL", "CARET", "EQUAL", "EQUAL_GT", "GT", - "GTE", "GT_GT", "HASH", "HASH_EQ", "HASH_GT", "HASH_GT_GT", - "HASH_HASH", "HYPHEN_GT", "HYPHEN_GT_GT", "HYPHEN_PIPE_HYPHEN", - "LT", "LTE", "LT_AT", "LT_CARET", "LT_GT", "LT_HYPHEN_GT", "LT_LT", - "LT_LT_EQ", "LT_QMARK_GT", "MINUS", "PERCENT", "PIPE", "PIPE_PIPE", - "PIPE_PIPE_SLASH", "PIPE_SLASH", "PLUS", "QMARK", "QMARK_AMP", - "QMARK_HASH", "QMARK_HYPHEN", "QMARK_PIPE", "SLASH", "TIL", - "TIL_EQ", "TIL_GTE_TIL", "TIL_GT_TIL", "TIL_LTE_TIL", "TIL_LT_TIL", - "TIL_STAR", "TIL_TIL", "SEMI", "ErrorCharacter" ] + symbolicNames = [ + "<INVALID>", + "BLOCK_COMMENT", + "LINE_COMMENT", + "WHITESPACE", + "TRUE", + "FALSE", + "FIELD", + "FIELDBYID", + "COMMA", + "COLON", + "COLON_COLON", + "DOLLAR", + "DOLLAR_DOLLAR", + "STAR", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACKET", + "CLOSE_BRACKET", + "BIT_STRING", + "REGEX_STRING", + "NUMERIC_LITERAL", + "INTEGER_LITERAL", + "HEX_INTEGER_LITERAL", + "DOT", + "SINGLEQ_STRING_LITERAL", + "DOUBLEQ_STRING_LITERAL", + "IDENTIFIER", + "IDENTIFIER_UNICODE", + "AMP", + "AMP_AMP", + "AMP_LT", + "AT_AT", + "AT_GT", + "AT_SIGN", + "BANG", + "BANG_BANG", + "BANG_EQUAL", + "CARET", + "EQUAL", + "EQUAL_GT", + "GT", + "GTE", + "GT_GT", + "HASH", + "HASH_EQ", + "HASH_GT", + "HASH_GT_GT", + "HASH_HASH", + "HYPHEN_GT", + "HYPHEN_GT_GT", + "HYPHEN_PIPE_HYPHEN", + "LT", + "LTE", + "LT_AT", + "LT_CARET", + "LT_GT", + "LT_HYPHEN_GT", + "LT_LT", + "LT_LT_EQ", + "LT_QMARK_GT", + "MINUS", + "PERCENT", + "PIPE", + "PIPE_PIPE", + "PIPE_PIPE_SLASH", + "PIPE_SLASH", + "PLUS", + "QMARK", + "QMARK_AMP", + "QMARK_HASH", + "QMARK_HYPHEN", + "QMARK_PIPE", + "SLASH", + "TIL", + "TIL_EQ", + "TIL_GTE_TIL", + "TIL_GT_TIL", + "TIL_LTE_TIL", + "TIL_LT_TIL", + "TIL_STAR", + "TIL_TIL", + "SEMI", + "ErrorCharacter", + ] - ruleNames = [ "WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", "A", "B", - "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", - "Y", "Z", "UNDERSCORE", "HEX_DIGIT", "DEC_DIGIT", "DQUOTA_STRING", - "SQUOTA_STRING", "BQUOTA_STRING", "TRUE", "FALSE", "FIELD", - "FIELDBYID", "COMMA", "COLON", "COLON_COLON", "DOLLAR", - "DOLLAR_DOLLAR", "STAR", "OPEN_PAREN", "CLOSE_PAREN", - "OPEN_BRACKET", "CLOSE_BRACKET", "BIT_STRING", "REGEX_STRING", - "NUMERIC_LITERAL", "INTEGER_LITERAL", "HEX_INTEGER_LITERAL", - "DOT", "SINGLEQ_STRING_LITERAL", "DOUBLEQ_STRING_LITERAL", - "IDENTIFIER", "IDENTIFIER_UNICODE", "AMP", "AMP_AMP", - "AMP_LT", "AT_AT", "AT_GT", "AT_SIGN", "BANG", "BANG_BANG", - "BANG_EQUAL", "CARET", "EQUAL", "EQUAL_GT", "GT", "GTE", - "GT_GT", "HASH", "HASH_EQ", "HASH_GT", "HASH_GT_GT", "HASH_HASH", - "HYPHEN_GT", "HYPHEN_GT_GT", "HYPHEN_PIPE_HYPHEN", "LT", - "LTE", "LT_AT", "LT_CARET", "LT_GT", "LT_HYPHEN_GT", "LT_LT", - "LT_LT_EQ", "LT_QMARK_GT", "MINUS", "PERCENT", "PIPE", - "PIPE_PIPE", "PIPE_PIPE_SLASH", "PIPE_SLASH", "PLUS", - "QMARK", "QMARK_AMP", "QMARK_HASH", "QMARK_HYPHEN", "QMARK_PIPE", - "SLASH", "TIL", "TIL_EQ", "TIL_GTE_TIL", "TIL_GT_TIL", - "TIL_LTE_TIL", "TIL_LT_TIL", "TIL_STAR", "TIL_TIL", "SEMI", - "ErrorCharacter" ] + ruleNames = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "UNDERSCORE", + "HEX_DIGIT", + "DEC_DIGIT", + "DQUOTA_STRING", + "SQUOTA_STRING", + "BQUOTA_STRING", + "BLOCK_COMMENT", + "LINE_COMMENT", + "WHITESPACE", + "TRUE", + "FALSE", + "FIELD", + "FIELDBYID", + "COMMA", + "COLON", + "COLON_COLON", + "DOLLAR", + "DOLLAR_DOLLAR", + "STAR", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACKET", + "CLOSE_BRACKET", + "BIT_STRING", + "REGEX_STRING", + "NUMERIC_LITERAL", + "INTEGER_LITERAL", + "HEX_INTEGER_LITERAL", + "DOT", + "SINGLEQ_STRING_LITERAL", + "DOUBLEQ_STRING_LITERAL", + "IDENTIFIER", + "IDENTIFIER_UNICODE", + "AMP", + "AMP_AMP", + "AMP_LT", + "AT_AT", + "AT_GT", + "AT_SIGN", + "BANG", + "BANG_BANG", + "BANG_EQUAL", + "CARET", + "EQUAL", + "EQUAL_GT", + "GT", + "GTE", + "GT_GT", + "HASH", + "HASH_EQ", + "HASH_GT", + "HASH_GT_GT", + "HASH_HASH", + "HYPHEN_GT", + "HYPHEN_GT_GT", + "HYPHEN_PIPE_HYPHEN", + "LT", + "LTE", + "LT_AT", + "LT_CARET", + "LT_GT", + "LT_HYPHEN_GT", + "LT_LT", + "LT_LT_EQ", + "LT_QMARK_GT", + "MINUS", + "PERCENT", + "PIPE", + "PIPE_PIPE", + "PIPE_PIPE_SLASH", + "PIPE_SLASH", + "PLUS", + "QMARK", + "QMARK_AMP", + "QMARK_HASH", + "QMARK_HYPHEN", + "QMARK_PIPE", + "SLASH", + "TIL", + "TIL_EQ", + "TIL_GTE_TIL", + "TIL_GT_TIL", + "TIL_LTE_TIL", + "TIL_LT_TIL", + "TIL_STAR", + "TIL_TIL", + "SEMI", + "ErrorCharacter", + ] grammarFileName = "BaserowFormulaLexer.g4" - def __init__(self, input=None, output:TextIO = sys.stdout): + def __init__(self, input=None, output: TextIO = sys.stdout): super().__init__(input, output) self.checkVersion("4.8") - self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self._interp = LexerATNSimulator( + self, self.atn, self.decisionsToDFA, PredictionContextCache() + ) self._actions = None self._predicates = None - - diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.tokens b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.tokens index 805982389..1b6673037 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.tokens +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaLexer.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaListener.py b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaListener.py index 4721090e8..b0979b4aa 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaListener.py +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaListener.py @@ -1,5 +1,6 @@ # Generated from BaserowFormula.g4 by ANTLR 4.8 from antlr4 import * + if __name__ is not None and "." in __name__: from .BaserowFormula import BaserowFormula else: @@ -9,121 +10,140 @@ else: class BaserowFormulaListener(ParseTreeListener): # Enter a parse tree produced by BaserowFormula#root. - def enterRoot(self, ctx:BaserowFormula.RootContext): + def enterRoot(self, ctx: BaserowFormula.RootContext): pass # Exit a parse tree produced by BaserowFormula#root. - def exitRoot(self, ctx:BaserowFormula.RootContext): + def exitRoot(self, ctx: BaserowFormula.RootContext): pass - # Enter a parse tree produced by BaserowFormula#FieldReference. - def enterFieldReference(self, ctx:BaserowFormula.FieldReferenceContext): + def enterFieldReference(self, ctx: BaserowFormula.FieldReferenceContext): pass # Exit a parse tree produced by BaserowFormula#FieldReference. - def exitFieldReference(self, ctx:BaserowFormula.FieldReferenceContext): + def exitFieldReference(self, ctx: BaserowFormula.FieldReferenceContext): pass - # Enter a parse tree produced by BaserowFormula#StringLiteral. - def enterStringLiteral(self, ctx:BaserowFormula.StringLiteralContext): + def enterStringLiteral(self, ctx: BaserowFormula.StringLiteralContext): pass # Exit a parse tree produced by BaserowFormula#StringLiteral. - def exitStringLiteral(self, ctx:BaserowFormula.StringLiteralContext): + def exitStringLiteral(self, ctx: BaserowFormula.StringLiteralContext): pass - # Enter a parse tree produced by BaserowFormula#Brackets. - def enterBrackets(self, ctx:BaserowFormula.BracketsContext): + def enterBrackets(self, ctx: BaserowFormula.BracketsContext): pass # Exit a parse tree produced by BaserowFormula#Brackets. - def exitBrackets(self, ctx:BaserowFormula.BracketsContext): + def exitBrackets(self, ctx: BaserowFormula.BracketsContext): pass - # Enter a parse tree produced by BaserowFormula#BooleanLiteral. - def enterBooleanLiteral(self, ctx:BaserowFormula.BooleanLiteralContext): + def enterBooleanLiteral(self, ctx: BaserowFormula.BooleanLiteralContext): pass # Exit a parse tree produced by BaserowFormula#BooleanLiteral. - def exitBooleanLiteral(self, ctx:BaserowFormula.BooleanLiteralContext): + def exitBooleanLiteral(self, ctx: BaserowFormula.BooleanLiteralContext): pass + # Enter a parse tree produced by BaserowFormula#RightWhitespaceOrComments. + def enterRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + pass + + # Exit a parse tree produced by BaserowFormula#RightWhitespaceOrComments. + def exitRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + pass # Enter a parse tree produced by BaserowFormula#DecimalLiteral. - def enterDecimalLiteral(self, ctx:BaserowFormula.DecimalLiteralContext): + def enterDecimalLiteral(self, ctx: BaserowFormula.DecimalLiteralContext): pass # Exit a parse tree produced by BaserowFormula#DecimalLiteral. - def exitDecimalLiteral(self, ctx:BaserowFormula.DecimalLiteralContext): + def exitDecimalLiteral(self, ctx: BaserowFormula.DecimalLiteralContext): pass + # Enter a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. + def enterLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + pass + + # Exit a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. + def exitLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + pass # Enter a parse tree produced by BaserowFormula#FunctionCall. - def enterFunctionCall(self, ctx:BaserowFormula.FunctionCallContext): + def enterFunctionCall(self, ctx: BaserowFormula.FunctionCallContext): pass # Exit a parse tree produced by BaserowFormula#FunctionCall. - def exitFunctionCall(self, ctx:BaserowFormula.FunctionCallContext): + def exitFunctionCall(self, ctx: BaserowFormula.FunctionCallContext): pass - # Enter a parse tree produced by BaserowFormula#FieldByIdReference. - def enterFieldByIdReference(self, ctx:BaserowFormula.FieldByIdReferenceContext): + def enterFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): pass # Exit a parse tree produced by BaserowFormula#FieldByIdReference. - def exitFieldByIdReference(self, ctx:BaserowFormula.FieldByIdReferenceContext): + def exitFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): pass - # Enter a parse tree produced by BaserowFormula#IntegerLiteral. - def enterIntegerLiteral(self, ctx:BaserowFormula.IntegerLiteralContext): + def enterIntegerLiteral(self, ctx: BaserowFormula.IntegerLiteralContext): pass # Exit a parse tree produced by BaserowFormula#IntegerLiteral. - def exitIntegerLiteral(self, ctx:BaserowFormula.IntegerLiteralContext): + def exitIntegerLiteral(self, ctx: BaserowFormula.IntegerLiteralContext): pass - # Enter a parse tree produced by BaserowFormula#BinaryOp. - def enterBinaryOp(self, ctx:BaserowFormula.BinaryOpContext): + def enterBinaryOp(self, ctx: BaserowFormula.BinaryOpContext): pass # Exit a parse tree produced by BaserowFormula#BinaryOp. - def exitBinaryOp(self, ctx:BaserowFormula.BinaryOpContext): + def exitBinaryOp(self, ctx: BaserowFormula.BinaryOpContext): pass + # Enter a parse tree produced by BaserowFormula#ws_or_comment. + def enterWs_or_comment(self, ctx: BaserowFormula.Ws_or_commentContext): + pass + + # Exit a parse tree produced by BaserowFormula#ws_or_comment. + def exitWs_or_comment(self, ctx: BaserowFormula.Ws_or_commentContext): + pass # Enter a parse tree produced by BaserowFormula#func_name. - def enterFunc_name(self, ctx:BaserowFormula.Func_nameContext): + def enterFunc_name(self, ctx: BaserowFormula.Func_nameContext): pass # Exit a parse tree produced by BaserowFormula#func_name. - def exitFunc_name(self, ctx:BaserowFormula.Func_nameContext): + def exitFunc_name(self, ctx: BaserowFormula.Func_nameContext): pass - # Enter a parse tree produced by BaserowFormula#field_reference. - def enterField_reference(self, ctx:BaserowFormula.Field_referenceContext): + def enterField_reference(self, ctx: BaserowFormula.Field_referenceContext): pass # Exit a parse tree produced by BaserowFormula#field_reference. - def exitField_reference(self, ctx:BaserowFormula.Field_referenceContext): + def exitField_reference(self, ctx: BaserowFormula.Field_referenceContext): pass - # Enter a parse tree produced by BaserowFormula#identifier. - def enterIdentifier(self, ctx:BaserowFormula.IdentifierContext): + def enterIdentifier(self, ctx: BaserowFormula.IdentifierContext): pass # Exit a parse tree produced by BaserowFormula#identifier. - def exitIdentifier(self, ctx:BaserowFormula.IdentifierContext): + def exitIdentifier(self, ctx: BaserowFormula.IdentifierContext): pass - -del BaserowFormula \ No newline at end of file +del BaserowFormula diff --git a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaVisitor.py b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaVisitor.py index 0241cc216..2fd3eb1c4 100644 --- a/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaVisitor.py +++ b/backend/src/baserow/contrib/database/formula/parser/generated/BaserowFormulaVisitor.py @@ -1,5 +1,6 @@ # Generated from BaserowFormula.g4 by ANTLR 4.8 from antlr4 import * + if __name__ is not None and "." in __name__: from .BaserowFormula import BaserowFormula else: @@ -7,72 +8,76 @@ else: # This class defines a complete generic visitor for a parse tree produced by BaserowFormula. + class BaserowFormulaVisitor(ParseTreeVisitor): # Visit a parse tree produced by BaserowFormula#root. - def visitRoot(self, ctx:BaserowFormula.RootContext): + def visitRoot(self, ctx: BaserowFormula.RootContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#FieldReference. - def visitFieldReference(self, ctx:BaserowFormula.FieldReferenceContext): + def visitFieldReference(self, ctx: BaserowFormula.FieldReferenceContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#StringLiteral. - def visitStringLiteral(self, ctx:BaserowFormula.StringLiteralContext): + def visitStringLiteral(self, ctx: BaserowFormula.StringLiteralContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#Brackets. - def visitBrackets(self, ctx:BaserowFormula.BracketsContext): + def visitBrackets(self, ctx: BaserowFormula.BracketsContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#BooleanLiteral. - def visitBooleanLiteral(self, ctx:BaserowFormula.BooleanLiteralContext): + def visitBooleanLiteral(self, ctx: BaserowFormula.BooleanLiteralContext): return self.visitChildren(ctx) + # Visit a parse tree produced by BaserowFormula#RightWhitespaceOrComments. + def visitRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + return self.visitChildren(ctx) # Visit a parse tree produced by BaserowFormula#DecimalLiteral. - def visitDecimalLiteral(self, ctx:BaserowFormula.DecimalLiteralContext): + def visitDecimalLiteral(self, ctx: BaserowFormula.DecimalLiteralContext): return self.visitChildren(ctx) + # Visit a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. + def visitLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + return self.visitChildren(ctx) # Visit a parse tree produced by BaserowFormula#FunctionCall. - def visitFunctionCall(self, ctx:BaserowFormula.FunctionCallContext): + def visitFunctionCall(self, ctx: BaserowFormula.FunctionCallContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#FieldByIdReference. - def visitFieldByIdReference(self, ctx:BaserowFormula.FieldByIdReferenceContext): + def visitFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#IntegerLiteral. - def visitIntegerLiteral(self, ctx:BaserowFormula.IntegerLiteralContext): + def visitIntegerLiteral(self, ctx: BaserowFormula.IntegerLiteralContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#BinaryOp. - def visitBinaryOp(self, ctx:BaserowFormula.BinaryOpContext): + def visitBinaryOp(self, ctx: BaserowFormula.BinaryOpContext): return self.visitChildren(ctx) + # Visit a parse tree produced by BaserowFormula#ws_or_comment. + def visitWs_or_comment(self, ctx: BaserowFormula.Ws_or_commentContext): + return self.visitChildren(ctx) # Visit a parse tree produced by BaserowFormula#func_name. - def visitFunc_name(self, ctx:BaserowFormula.Func_nameContext): + def visitFunc_name(self, ctx: BaserowFormula.Func_nameContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#field_reference. - def visitField_reference(self, ctx:BaserowFormula.Field_referenceContext): + def visitField_reference(self, ctx: BaserowFormula.Field_referenceContext): return self.visitChildren(ctx) - # Visit a parse tree produced by BaserowFormula#identifier. - def visitIdentifier(self, ctx:BaserowFormula.IdentifierContext): + def visitIdentifier(self, ctx: BaserowFormula.IdentifierContext): return self.visitChildren(ctx) - -del BaserowFormula \ No newline at end of file +del BaserowFormula diff --git a/backend/src/baserow/contrib/database/formula/parser/parser.py b/backend/src/baserow/contrib/database/formula/parser/parser.py index 1da734412..06630974f 100644 --- a/backend/src/baserow/contrib/database/formula/parser/parser.py +++ b/backend/src/baserow/contrib/database/formula/parser/parser.py @@ -1,11 +1,29 @@ -from antlr4 import InputStream +from antlr4 import InputStream, CommonTokenStream from antlr4.BufferedTokenStream import BufferedTokenStream +from antlr4.error.ErrorListener import ErrorListener +from baserow.contrib.database.formula.parser.exceptions import BaserowFormulaSyntaxError +from baserow.contrib.database.formula.parser.generated.BaserowFormula import ( + BaserowFormula, +) from baserow.contrib.database.formula.parser.generated.BaserowFormulaLexer import ( BaserowFormulaLexer, ) +class BaserowFormulaErrorListener(ErrorListener): + """ + A custom error listener as ANTLR's default error listen does not raise an + exception if a syntax error is found in a parse tree. + """ + + # noinspection PyPep8Naming + def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): + msg = msg.replace("<EOF>", "the end of the formula") + message = f"Invalid syntax at line {line}, col {column}: {msg}" + raise BaserowFormulaSyntaxError(message) + + def get_token_stream_for_formula(formula: str) -> BufferedTokenStream: lexer = BaserowFormulaLexer(InputStream(formula)) stream = BufferedTokenStream(lexer) @@ -14,7 +32,22 @@ def get_token_stream_for_formula(formula: str) -> BufferedTokenStream: return stream +def get_parse_tree_for_formula(formula: str): + lexer = BaserowFormulaLexer(InputStream(formula)) + stream = CommonTokenStream(lexer) + parser = BaserowFormula(stream) + parser.removeErrorListeners() + parser.addErrorListener(BaserowFormulaErrorListener()) + return parser.root() + + def convert_string_literal_token_to_string(string_literal, is_single_q): literal_without_outer_quotes = string_literal[1:-1] quote = "'" if is_single_q else '"' return literal_without_outer_quotes.replace("\\" + quote, quote) + + +def convert_string_to_string_literal_token(string, is_single_q): + quote = "'" if is_single_q else '"' + escaped = string.replace(quote, "\\" + quote) + return quote + escaped + quote diff --git a/backend/src/baserow/contrib/database/formula/parser/replace_field_by_id_with_field.py b/backend/src/baserow/contrib/database/formula/parser/replace_field_by_id_with_field.py deleted file mode 100644 index c62fc4ab4..000000000 --- a/backend/src/baserow/contrib/database/formula/parser/replace_field_by_id_with_field.py +++ /dev/null @@ -1,95 +0,0 @@ -from io import StringIO -from typing import Dict - -from antlr4 import Token - -from baserow.contrib.database.formula.parser.generated.BaserowFormulaLexer import ( - BaserowFormulaLexer, -) -from baserow.contrib.database.formula.parser.parser import get_token_stream_for_formula - - -# Translated directly from replaceFieldByIdWithField.js please keep in sync -# if changes made. -def replace_field_by_id_with_field( - formula: str, field_id_to_name: Dict[int, str] -) -> str: - """ - Given a baserow formula transforms any field_by_id(X) references to field(NAME) - when there is a mapping in field_id_to_name of {X: NAME}. If no mapping is found - then the field_by_id is left untransformed. Preserves whitespace and comments in - the returned string. - - :param formula: A string possibly in the Baserow Formula language. - :param field_id_to_name: A mapping of field ids to field names to replace in the - formula. - :return: A formula string with replacements done. - """ - - stream = get_token_stream_for_formula(formula) - - searching_for_open_paren = False - searching_for_close_paren = False - searching_for_integer_literal = False - - with StringIO() as buf: - for i in range(0, len(stream.tokens)): - t = stream.tokens[i] - output = t.text - - is_normal_token = t.channel == 0 - if is_normal_token: - if searching_for_integer_literal: - searching_for_integer_literal = False - searching_for_close_paren = True - if t.type == BaserowFormulaLexer.INTEGER_LITERAL: - int_literal = int(t.text) - field_name = field_id_to_name[int_literal] - escaped_field_name = field_name.replace("'", "\\'") - output = f"'{escaped_field_name}'" - else: - return formula - elif searching_for_open_paren: - searching_for_open_paren = False - if t.type == BaserowFormulaLexer.OPEN_PAREN: - searching_for_integer_literal = True - else: - return formula - elif searching_for_close_paren: - searching_for_close_paren = False - if t.type != BaserowFormulaLexer.CLOSE_PAREN: - return formula - elif t.type == BaserowFormulaLexer.FIELDBYID: - looked_ahead_id = _lookahead_to_id(i + 1, stream) - if ( - looked_ahead_id is not None - and looked_ahead_id in field_id_to_name - ): - output = "field" - searching_for_open_paren = True - - if t.type == Token.EOF: - break - - buf.write(output) - return buf.getvalue() - - -def _lookahead_to_id(start, stream): - searching_for_int_literal = False - for i in range(start, len(stream.tokens)): - t = stream.tokens[i] - is_normal_token = t.channel == 0 - if is_normal_token: - if searching_for_int_literal: - if t.type == BaserowFormulaLexer.INTEGER_LITERAL: - return int(t.text) - else: - return None - elif t.type == BaserowFormulaLexer.OPEN_PAREN: - searching_for_int_literal = True - else: - return None - if t.type == Token.EOF: - return None - return None diff --git a/backend/src/baserow/contrib/database/formula/parser/replace_field_with_field_by_id.py b/backend/src/baserow/contrib/database/formula/parser/replace_field_with_field_by_id.py deleted file mode 100644 index 07772a5dc..000000000 --- a/backend/src/baserow/contrib/database/formula/parser/replace_field_with_field_by_id.py +++ /dev/null @@ -1,123 +0,0 @@ -from io import StringIO -from typing import Dict - -from antlr4 import Token - -from baserow.contrib.database.formula.ast.exceptions import UnknownFieldReference -from baserow.contrib.database.formula.parser.generated.BaserowFormulaLexer import ( - BaserowFormulaLexer, -) -from baserow.contrib.database.formula.parser.parser import ( - get_token_stream_for_formula, - convert_string_literal_token_to_string, -) - - -def _replace_field_name_in_string_literal_or_raise_if_unknown( - field_ref_string_literal: str, - is_single_q: bool, - field_name_to_field_id: Dict[str, int], -) -> str: - unescaped_old_name = convert_string_literal_token_to_string( - field_ref_string_literal, is_single_q - ) - - field_id = field_name_to_field_id.get(unescaped_old_name, None) - if field_id is not None: - return str(field_id) - else: - raise UnknownFieldReference(unescaped_old_name) - - -def replace_field_with_field_by_id( - formula: str, field_name_to_field_id: Dict[str, int] -) -> str: - """ - Given a baserow formula transforms any field(NAME) references to field_by_id(X) - when there is a mapping in field_name_to_field_id of {NAME: X}. If no mapping is - found then a UnknownFieldReference exception is raised. Preserves whitespace and - comments in the returned string. - - :param formula: A string possibly in the Baserow Formula language. - :param field_name_to_field_id: A mapping of field names to field ids to replace in - the formula. - :return: A formula string with replacements done. - :raises UnknownFieldReference: When a field(NAME) is found in the formula but NAME - is not present in field_name_to_field_id. - """ - - stream = get_token_stream_for_formula(formula) - - searching_for_open_paren = False - searching_for_inner_field_reference_string_literal = False - searching_for_close_paren = False - - with StringIO() as buf: - for i in range(0, len(stream.tokens)): - t = stream.tokens[i] - output = t.text - is_normal_token = t.channel == 0 - if is_normal_token: - if searching_for_inner_field_reference_string_literal: - searching_for_inner_field_reference_string_literal = False - searching_for_close_paren = True - if t.type == BaserowFormulaLexer.SINGLEQ_STRING_LITERAL: - output = ( - _replace_field_name_in_string_literal_or_raise_if_unknown( - output, True, field_name_to_field_id - ) - ) - elif t.type == BaserowFormulaLexer.DOUBLEQ_STRING_LITERAL: - output = ( - _replace_field_name_in_string_literal_or_raise_if_unknown( - output, False, field_name_to_field_id - ) - ) - else: - return formula - elif searching_for_open_paren: - searching_for_open_paren = False - if t.type == BaserowFormulaLexer.OPEN_PAREN: - searching_for_inner_field_reference_string_literal = True - else: - return formula - elif searching_for_close_paren: - searching_for_close_paren = False - if t.type != BaserowFormulaLexer.CLOSE_PAREN: - return formula - elif t.type == BaserowFormulaLexer.FIELD: - future_string_literal = _lookahead_to_name(i + 1, stream) - if ( - future_string_literal is not None - and future_string_literal in field_name_to_field_id - ): - searching_for_open_paren = True - output = "field_by_id" - - if t.type == Token.EOF: - break - - buf.write(output) - return buf.getvalue() - - -def _lookahead_to_name(start, stream): - searching_for_string_literal = False - for i in range(start, len(stream.tokens)): - t = stream.tokens[i] - is_normal_token = t.channel == 0 - if is_normal_token: - if searching_for_string_literal: - if t.type == BaserowFormulaLexer.SINGLEQ_STRING_LITERAL: - return convert_string_literal_token_to_string(t.text, True) - elif t.type == BaserowFormulaLexer.DOUBLEQ_STRING_LITERAL: - return convert_string_literal_token_to_string(t.text, False) - else: - return None - elif t.type == BaserowFormulaLexer.OPEN_PAREN: - searching_for_string_literal = True - else: - return None - if t.type == Token.EOF: - return None - return None diff --git a/backend/src/baserow/contrib/database/formula/parser/update_field_names.py b/backend/src/baserow/contrib/database/formula/parser/update_field_names.py new file mode 100644 index 000000000..4d7dd6f0b --- /dev/null +++ b/backend/src/baserow/contrib/database/formula/parser/update_field_names.py @@ -0,0 +1,149 @@ +from typing import Dict, Optional + +from baserow.contrib.database.formula.parser.exceptions import ( + MaximumFormulaSizeError, +) +from baserow.contrib.database.formula.parser.generated.BaserowFormula import ( + BaserowFormula, +) +from baserow.contrib.database.formula.parser.generated.BaserowFormulaVisitor import ( + BaserowFormulaVisitor, +) +from baserow.contrib.database.formula.parser.parser import ( + convert_string_literal_token_to_string, + convert_string_to_string_literal_token, + get_parse_tree_for_formula, +) + + +# noinspection DuplicatedCode +class UpdateFieldNameFormulaVisitor(BaserowFormulaVisitor): + """ + Visits nodes of the BaserowFormula antlr parse tree returning the formula in string + form, but with field(name) and field_by_id(id) references replaced according to the + input dictionaries. + """ + + def __init__( + self, + field_names_to_update: Optional[Dict[str, str]] = None, + field_ids_to_replace_with_name_refs: Optional[Dict[int, str]] = None, + field_names_to_replace_with_id_refs: Optional[Dict[str, int]] = None, + ): + if field_names_to_update is None: + field_names_to_update = {} + if field_ids_to_replace_with_name_refs is None: + field_ids_to_replace_with_name_refs = {} + if field_names_to_replace_with_id_refs is None: + field_names_to_replace_with_id_refs = {} + + self.field_names_to_replace_with_id_refs = field_names_to_replace_with_id_refs + self.field_names_to_update = field_names_to_update + self.field_ids_to_replace_with_name_refs = field_ids_to_replace_with_name_refs + + def visitRoot(self, ctx: BaserowFormula.RootContext): + return ctx.expr().accept(self) + + def visitStringLiteral(self, ctx: BaserowFormula.StringLiteralContext): + return ctx.getText() + + def visitDecimalLiteral(self, ctx: BaserowFormula.DecimalLiteralContext): + return ctx.getText() + + def visitBooleanLiteral(self, ctx: BaserowFormula.BooleanLiteralContext): + return ctx.getText() + + def visitBrackets(self, ctx: BaserowFormula.BracketsContext): + return ctx.expr().accept(self) + + def visitFunctionCall(self, ctx: BaserowFormula.FunctionCallContext): + function_name = ctx.func_name().getText() + args = [expr.accept(self) for expr in (ctx.expr())] + args_with_any_field_names_replaced = ",".join(args) + return f"{function_name}({args_with_any_field_names_replaced})" + + def visitBinaryOp(self, ctx: BaserowFormula.BinaryOpContext): + args = [expr.accept(self) for expr in (ctx.expr())] + return args[0] + ctx.op.text + args[1] + + def visitFunc_name(self, ctx: BaserowFormula.Func_nameContext): + return ctx.getText() + + def visitIdentifier(self, ctx: BaserowFormula.IdentifierContext): + return ctx.getText() + + def visitIntegerLiteral(self, ctx: BaserowFormula.IntegerLiteralContext): + return ctx.getText() + + def visitFieldReference(self, ctx: BaserowFormula.FieldReferenceContext): + reference = ctx.field_reference() + is_single_quote_ref = reference.SINGLEQ_STRING_LITERAL() + field_name = convert_string_literal_token_to_string( + reference.getText(), is_single_quote_ref + ) + if field_name in self.field_names_to_update: + new_name = self.field_names_to_update[field_name] + escaped_new_name = convert_string_to_string_literal_token( + new_name, is_single_quote_ref + ) + field = ctx.FIELD().getText() + return f"{field}({escaped_new_name})" + elif field_name in self.field_names_to_replace_with_id_refs: + return ( + f"field_by_id({self.field_names_to_replace_with_id_refs[field_name]})" + ) + else: + return ctx.getText() + + def visitFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): + field_id = int(str(ctx.INTEGER_LITERAL())) + if field_id not in self.field_ids_to_replace_with_name_refs: + return f"field('unknown field {field_id}')" + new_name = self.field_ids_to_replace_with_name_refs[field_id] + escaped_new_name = convert_string_to_string_literal_token(new_name, True) + return f"field({escaped_new_name})" + + def visitLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + updated_expr = ctx.expr().accept(self) + return ctx.ws_or_comment().getText() + updated_expr + + def visitRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + updated_expr = ctx.expr().accept(self) + return updated_expr + ctx.ws_or_comment().getText() + + +def update_field_names( + formula: str, + field_names_to_update: Optional[Dict[str, str]] = None, + field_ids_to_replace_with_name_refs: Optional[Dict[int, str]] = None, + field_names_to_replace_with_id_refs: Optional[Dict[str, int]] = None, +) -> str: + """ + :param formula: The raw formula string to update field names in. + :param field_names_to_update: A dictionary where the keys are the old + field names with the values being the new names to replace the old with. + :param field_ids_to_replace_with_name_refs: To replace field_by_id references + with specific field names then provide this dictionary of field id to name + which will swap field_by_id(key) for a field(value). If a field id is not found + in this dict it will be swapped for field('unknown field {id}'). + :param field_names_to_replace_with_id_refs: To replace field references + with specific field ids then provide this dictionary of field name to id + which will swap field(key) for a field_by_id(value). If a field name is not + found in this dict it will be left alone. + :return: An updated formula string where field and field_by_id references have + been updated accordingly. Whitespace and comments will not have been modified. + """ + + try: + tree = get_parse_tree_for_formula(formula) + return UpdateFieldNameFormulaVisitor( + field_names_to_update, + field_ids_to_replace_with_name_refs, + field_names_to_replace_with_id_refs, + ).visit(tree) + except RecursionError: + raise MaximumFormulaSizeError() diff --git a/backend/src/baserow/contrib/database/formula/types/table_typer.py b/backend/src/baserow/contrib/database/formula/types/table_typer.py index 6b3dc1ac7..5aea98942 100644 --- a/backend/src/baserow/contrib/database/formula/types/table_typer.py +++ b/backend/src/baserow/contrib/database/formula/types/table_typer.py @@ -9,31 +9,30 @@ from baserow.contrib.database.fields.models import ( ) from baserow.contrib.database.fields.registries import field_type_registry, FieldType from baserow.contrib.database.formula.ast.tree import ( - BaserowFieldByIdReference, BaserowExpression, BaserowFunctionDefinition, + BaserowFieldReference, ) from baserow.contrib.database.formula.parser.ast_mapper import ( raw_formula_to_untyped_expression, - replace_field_refs_according_to_new_or_deleted_fields, ) from baserow.contrib.database.formula.parser.exceptions import MaximumFormulaSizeError from baserow.contrib.database.formula.types.exceptions import ( NoSelfReferencesError, NoCircularReferencesError, ) -from baserow.contrib.database.formula.types.formula_types import ( - BASEROW_FORMULA_TYPE_ALLOWED_FIELDS, -) from baserow.contrib.database.formula.types.formula_type import ( BaserowFormulaType, BaserowFormulaValidType, UnTyped, ) +from baserow.contrib.database.formula.types.formula_types import ( + BASEROW_FORMULA_TYPE_ALLOWED_FIELDS, +) from baserow.contrib.database.formula.types.visitors import ( FieldReferenceResolvingVisitor, TypeAnnotatingASTVisitor, - SubstituteFieldByIdWithThatFieldsExpressionVisitor, + SubstituteFieldWithThatFieldsExpressionVisitor, FunctionsUsedVisitor, ) from baserow.contrib.database.table import models @@ -43,7 +42,7 @@ def _get_all_fields_and_build_name_dict( table: "models.Table", overridden_field: Optional[Field] ): all_fields = [] - field_name_to_id = {} + field_name_to_db_column = {} for field in table.field_set.all(): if overridden_field and field.id == overridden_field.id: extracted_field = overridden_field @@ -51,47 +50,39 @@ def _get_all_fields_and_build_name_dict( extracted_field = field extracted_field = extracted_field.specific all_fields.append(extracted_field) - field_name_to_id[extracted_field.name] = extracted_field.id - return all_fields, field_name_to_id + field_name_to_db_column[extracted_field.name] = extracted_field.db_column + return all_fields, field_name_to_db_column -def _fix_deleted_or_new_refs_in_formula_and_parse_into_untyped_formula( +def _parse_formula_string_to_untyped_expression( field: FormulaField, - deleted_field_id_to_name: Dict[int, str], - field_name_to_id: Dict[str, int], + field_name_to_db_column: Dict[str, str], ): - fixed_formula = replace_field_refs_according_to_new_or_deleted_fields( - field.formula, deleted_field_id_to_name, field_name_to_id - ) + untyped_expression = raw_formula_to_untyped_expression( - fixed_formula, set(field_name_to_id.values()) + field.formula, field_name_to_db_column ) - return UntypedFormulaFieldWithReferences(field, fixed_formula, untyped_expression) + + return UntypedFormulaFieldWithReferences(field, untyped_expression) class UntypedFormulaFieldWithReferences: """ A graph node class, containing a formula field and it's untyped but parsed BaserowExpression, references to it's child and parent fields and it's formula - field with any field/field_by_id transformations applied already. + field. """ def __init__( self, original_formula_field: FormulaField, - fixed_raw_formula: str, untyped_expression: BaserowExpression[UnTyped], ): self.original_formula_field = original_formula_field self.untyped_expression = untyped_expression - self.fixed_raw_formula = fixed_raw_formula - self.parents: Dict[int, "UntypedFormulaFieldWithReferences"] = {} - self.formula_children: Dict[int, "UntypedFormulaFieldWithReferences"] = {} - self.field_children: Set[int] = set() - - @property - def field_id(self): - return self.original_formula_field.id + self.parents: Dict[str, "UntypedFormulaFieldWithReferences"] = {} + self.formula_children: Dict[str, "UntypedFormulaFieldWithReferences"] = {} + self.field_children: Set[str] = set() @property def field_name(self): @@ -107,19 +98,19 @@ class UntypedFormulaFieldWithReferences: :param child_formula: The new child to register to this node. """ - if child_formula.field_id == self.field_id: + if child_formula.field_name == self.field_name: raise NoSelfReferencesError() - self.formula_children[child_formula.field_id] = child_formula - child_formula.parents[self.field_id] = self + self.formula_children[child_formula.field_name] = child_formula + child_formula.parents[self.field_name] = self def add_child_field(self, child): self.field_children.add(child) def add_all_children_depth_first_order_raise_for_circular_ref( self, - visited_so_far: OrderedDictType[int, "UntypedFormulaFieldWithReferences"], + visited_so_far: OrderedDictType[str, "UntypedFormulaFieldWithReferences"], ordered_formula_fields: OrderedDictType[ - int, "UntypedFormulaFieldWithReferences" + str, "UntypedFormulaFieldWithReferences" ], ): """ @@ -134,39 +125,38 @@ class UntypedFormulaFieldWithReferences: children appear in the list before their parents. """ - if self.field_id in visited_so_far: + if self.field_name in visited_so_far: raise NoCircularReferencesError( [f.field_name for f in visited_so_far.values()] + [self.field_name] ) - visited_so_far[self.field_id] = self - if self.field_id in ordered_formula_fields: + visited_so_far[self.field_name] = self + if self.field_name in ordered_formula_fields: return for formula_child in self.formula_children.values(): formula_child.add_all_children_depth_first_order_raise_for_circular_ref( visited_so_far.copy(), ordered_formula_fields, ) - ordered_formula_fields[self.field_id] = self + ordered_formula_fields[self.field_name] = self def to_typed( self, typed_expression: BaserowExpression[BaserowFormulaType], - field_id_to_typed_field: Dict[int, "TypedFieldWithReferences"], + field_name_to_typed_field: Dict[str, "TypedFieldWithReferences"], ) -> "TypedFieldWithReferences": """ Given a typed expression for this field generates a TypedFieldWithReferences graph node. :param typed_expression: The typed expression for this field. - :param field_id_to_typed_field: A dictionary of field id to other + :param field_name_to_typed_field: A dictionary of field name to other TypedFieldWithReferences which must already contain all child fields of this field. :return: A new TypedFieldWithReferences based off this field. """ updated_formula_field = self._create_untyped_copy_of_original_field() - updated_formula_field.formula = self.fixed_raw_formula functions_used: Set[BaserowFunctionDefinition] = typed_expression.accept( FunctionsUsedVisitor() @@ -185,9 +175,9 @@ class UntypedFormulaFieldWithReferences: ) for field_child in self.field_children: - typed_field.add_child(field_id_to_typed_field[field_child]) + typed_field.add_child(field_name_to_typed_field[field_child]) for formula_child in self.formula_children.values(): - typed_field.add_child(field_id_to_typed_field[formula_child.field_id]) + typed_field.add_child(field_name_to_typed_field[formula_child.field_name]) return typed_field @@ -200,14 +190,14 @@ class UntypedFormulaFieldWithReferences: def _add_children_to_untyped_formula_raising_if_self_ref_found( untyped_formula_field: UntypedFormulaFieldWithReferences, - field_id_to_untyped_formula: Dict[int, UntypedFormulaFieldWithReferences], + field_name_to_untyped_formula: Dict[str, UntypedFormulaFieldWithReferences], ): children = untyped_formula_field.untyped_expression.accept( FieldReferenceResolvingVisitor() ) for child in children: - if child in field_id_to_untyped_formula: - child_formula = field_id_to_untyped_formula[child] + if child in field_name_to_untyped_formula: + child_formula = field_name_to_untyped_formula[child] untyped_formula_field.add_child_formulas_and_raise_if_self_ref_found( child_formula ) @@ -216,10 +206,10 @@ def _add_children_to_untyped_formula_raising_if_self_ref_found( def _find_formula_field_type_resolution_order_and_raise_if_circular_ref_found( - field_id_to_untyped_formula: Dict[int, UntypedFormulaFieldWithReferences] -) -> typing.OrderedDict[int, UntypedFormulaFieldWithReferences]: + field_id_to_untyped_formula: Dict[str, UntypedFormulaFieldWithReferences] +) -> typing.OrderedDict[str, UntypedFormulaFieldWithReferences]: ordered_untyped_formulas: OrderedDict[ - int, UntypedFormulaFieldWithReferences + str, UntypedFormulaFieldWithReferences ] = OrderedDict() for untyped_formula in field_id_to_untyped_formula.values(): untyped_formula.add_all_children_depth_first_order_raise_for_circular_ref( @@ -233,8 +223,8 @@ def _calculate_non_formula_field_typed_expression( ): field_type: FieldType = field_type_registry.get_by_model(field) formula_type = field_type.to_baserow_formula_type(field) - typed_expr = BaserowFieldByIdReference[BaserowFormulaValidType]( - field.id, formula_type + typed_expr = BaserowFieldReference[BaserowFormulaValidType]( + field.name, field.db_column, formula_type ) return TypedFieldWithReferences(field, field, typed_expr, False) @@ -257,41 +247,41 @@ class TypedFieldWithReferences: self.original_field = original_field self.new_field = updated_field self.typed_expression = typed_expression - self.children: Dict[int, "TypedFieldWithReferences"] = {} - self.parents: Dict[int, "TypedFieldWithReferences"] = {} + self.children: Dict[str, "TypedFieldWithReferences"] = {} + self.parents: Dict[str, "TypedFieldWithReferences"] = {} @property def formula_type(self) -> BaserowFormulaType: return self.typed_expression.expression_type @property - def field_id(self) -> int: - return self.new_field.id + def field_name(self) -> str: + return self.new_field.name def add_all_missing_valid_parents( self, - other_changed_fields: Dict[int, Field], - field_id_to_typed_field: Dict[int, "TypedFieldWithReferences"], + other_changed_fields: Dict[str, Field], + field_name_to_typed_field: Dict[str, "TypedFieldWithReferences"], ): for parent in self.parents.values(): - typed_parent_field = field_id_to_typed_field[parent.field_id] + typed_parent_field = field_name_to_typed_field[parent.field_name] if typed_parent_field.formula_type.is_valid: typed_parent_field.add_all_missing_valid_parents( - other_changed_fields, field_id_to_typed_field + other_changed_fields, field_name_to_typed_field ) - other_changed_fields[parent.field_id] = typed_parent_field.new_field + other_changed_fields[parent.field_name] = typed_parent_field.new_field def get_all_child_fields_not_already_found_recursively( - self, already_found_field_ids: Set[int] + self, already_found_field_names: Set[str] ) -> List[Field]: all_not_found_already_child_fields = [] for child_field_id, child in self.children.items(): - if child_field_id not in already_found_field_ids: - already_found_field_ids.add(child_field_id) + if child_field_id not in already_found_field_names: + already_found_field_names.add(child_field_id) recursive_child_fields = ( child.get_all_child_fields_not_already_found_recursively( - already_found_field_ids + already_found_field_names ) ) all_not_found_already_child_fields += [ @@ -300,18 +290,18 @@ class TypedFieldWithReferences: return all_not_found_already_child_fields def add_child(self, child: "TypedFieldWithReferences"): - self.children[child.field_id] = child - child.parents[self.field_id] = self + self.children[child.field_name] = child + child.parents[self.field_name] = self def _type_and_substitute_formula_field( untyped_formula: UntypedFormulaFieldWithReferences, - field_id_to_typed_expression: Dict[int, TypedFieldWithReferences], + field_name_to_typed_field: Dict[str, TypedFieldWithReferences], ): typed_expr: BaserowExpression[ BaserowFormulaType ] = untyped_formula.untyped_expression.accept( - TypeAnnotatingASTVisitor(field_id_to_typed_expression) + TypeAnnotatingASTVisitor(field_name_to_typed_field) ) merged_expression_type = ( @@ -333,60 +323,51 @@ def _type_and_substitute_formula_field( # is guaranteed to only contain references to static normal fields meaning we # can evaluate the result of this formula in one shot instead of having to evaluate # all depended on formula fields in turn to then calculate this one. - typed_expr_with_substituted_field_by_id_references = wrapped_typed_expr.accept( - SubstituteFieldByIdWithThatFieldsExpressionVisitor(field_id_to_typed_expression) + typed_expr_with_substituted_field_references = wrapped_typed_expr.accept( + SubstituteFieldWithThatFieldsExpressionVisitor(field_name_to_typed_field) ) - return typed_expr_with_substituted_field_by_id_references + return typed_expr_with_substituted_field_references def type_all_fields_in_table( table: "models.Table", - deleted_field_id_to_name: Optional[Dict[int, str]] = None, overridden_field: Optional[Field] = None, -) -> Dict[int, TypedFieldWithReferences]: +) -> Dict[str, TypedFieldWithReferences]: """ The key algorithm responsible for typing a table in Baserow. :param table: The table to find Baserow Formula Types for every field. - :param deleted_field_id_to_name: A map of field id's to field names which should be - provided when a field has just been deleted. It should contain the deleted - fields old id and its name prior to deletion. This is used to correctly replace - any field_by_id references to that deleted field with field(name) references - instead. :param overridden_field: An optional field instance which will be used instead of that field's current database value when typing the table. - :return: A dictionary of field id to a wrapper object TypedFieldWithReferences + :return: A dictionary of field name to a wrapper object TypedFieldWithReferences containing type and reference information about that field. """ try: - if deleted_field_id_to_name is None: - deleted_field_id_to_name = {} - # Step 1. Preprocess every field: # We go over all the fields, parse formula fields, fix any - # references to deleted fields, replace any field references with - # field_by_id and finally store type information for non formula fields. + # references to renamed fields and finally store type information for non + # formula fields. ( - field_id_to_untyped_formula, - field_id_to_updated_typed_field, - ) = _fix_and_parse_all_fields(deleted_field_id_to_name, table, overridden_field) + field_name_to_untyped_formula, + field_name_to_typed_field, + ) = _parse_all_fields(table, overridden_field) # Step 2. Construct the graph of field dependencies by: # For every untyped formula populate its list of children with # references to formulas it depends on. - for untyped_formula in field_id_to_untyped_formula.values(): + for untyped_formula in field_name_to_untyped_formula.values(): _add_children_to_untyped_formula_raising_if_self_ref_found( - untyped_formula, field_id_to_untyped_formula + untyped_formula, field_name_to_untyped_formula ) # Step 3. Order the formula fields using the graph so we can type them: # Now using the graph of field dependencies we build an ordering of # the formula fields so that any field that is depended on by another field # comes earlier in the list than it's parent. - formula_field_ids_ordered_by_typing_order = ( + formula_fields_ordered_by_typing_order = ( _find_formula_field_type_resolution_order_and_raise_if_circular_ref_found( - field_id_to_untyped_formula + field_name_to_untyped_formula ) ) @@ -398,46 +379,45 @@ def type_all_fields_in_table( for ( formula_id, untyped_formula, - ) in formula_field_ids_ordered_by_typing_order.items(): + ) in formula_fields_ordered_by_typing_order.items(): typed_expr = _type_and_substitute_formula_field( - untyped_formula, field_id_to_updated_typed_field + untyped_formula, field_name_to_typed_field ) - field_id = untyped_formula.field_id - field_id_to_updated_typed_field[field_id] = untyped_formula.to_typed( - typed_expr, field_id_to_updated_typed_field + field_name = untyped_formula.field_name + field_name_to_typed_field[field_name] = untyped_formula.to_typed( + typed_expr, field_name_to_typed_field ) - return field_id_to_updated_typed_field + return field_name_to_typed_field except RecursionError: raise MaximumFormulaSizeError() -def _fix_and_parse_all_fields( - deleted_field_id_to_name: Dict[int, str], +def _parse_all_fields( table: "models.Table", overridden_field: Optional[Field], ): - all_fields, field_name_to_id = _get_all_fields_and_build_name_dict( + all_fields, field_name_to_db_column = _get_all_fields_and_build_name_dict( table, overridden_field ) - field_id_to_untyped_formula: Dict[int, UntypedFormulaFieldWithReferences] = {} - field_id_to_updated_typed_field: Dict[int, TypedFieldWithReferences] = {} + field_name_to_untyped_formula: Dict[str, UntypedFormulaFieldWithReferences] = {} + field_name_to_updated_typed_field: Dict[str, TypedFieldWithReferences] = {} for field in all_fields: specific_class = field.specific_class field_type = field_type_registry.get_by_model(specific_class) - field_id = field.id + field_name = field.name if field_type.type == "formula": - field_id_to_untyped_formula[ - field_id - ] = _fix_deleted_or_new_refs_in_formula_and_parse_into_untyped_formula( - field, deleted_field_id_to_name, field_name_to_id + field_name_to_untyped_formula[ + field_name + ] = _parse_formula_string_to_untyped_expression( + field, field_name_to_db_column ) else: updated_typed_field = _calculate_non_formula_field_typed_expression(field) - field_id_to_updated_typed_field[field_id] = updated_typed_field - return field_id_to_untyped_formula, field_id_to_updated_typed_field + field_name_to_updated_typed_field[field_name] = updated_typed_field + return field_name_to_untyped_formula, field_name_to_updated_typed_field class TypedBaserowTable: @@ -446,27 +426,27 @@ class TypedBaserowTable: references for all fields in a table. """ - def __init__(self, typed_fields: Dict[int, TypedFieldWithReferences]): + def __init__(self, typed_fields: Dict[str, TypedFieldWithReferences]): self.typed_fields_with_references = typed_fields def get_all_depended_on_fields( - self, field: Field, field_ids_to_ignore: Set[int] + self, field: Field, field_names_to_ignore: Set[str] ) -> List[Field]: """ Returns all other fields not already present in the field_ids_to_ignore set for which field depends on to calculate it's value. :param field: The field to get all dependant fields for. - :param field_ids_to_ignore: A set of field ids to ignore, will be updated with + :param field_names_to_ignore: A set of field ids to ignore, will be updated with all the field id's of fields returned by this call. :return: A list of field instances for which field depends on. """ - if field.id not in self.typed_fields_with_references: + if field.name not in self.typed_fields_with_references: return [] - typed_field = self.typed_fields_with_references[field.id] + typed_field = self.typed_fields_with_references[field.name] return typed_field.get_all_child_fields_not_already_found_recursively( - field_ids_to_ignore + field_names_to_ignore ) def get_typed_field(self, field: Field) -> Optional[TypedFieldWithReferences]: @@ -476,17 +456,17 @@ class TypedBaserowTable: None. """ - if field.id not in self.typed_fields_with_references: + if field.name not in self.typed_fields_with_references: return None - return self.typed_fields_with_references[field.id] + return self.typed_fields_with_references[field.name] - def get_typed_field_instance(self, field_id: int) -> Field: + def get_typed_field_instance(self, field_name: str) -> Field: """ - :param field_id: The field id to get its newly typed field for. + :param field_name: The field name get its newly typed field for. :return: The updated field instance after typing. """ - return self.typed_fields_with_references[field_id].new_field + return self.typed_fields_with_references[field_name].new_field def type_table( diff --git a/backend/src/baserow/contrib/database/formula/types/typed_field_updater.py b/backend/src/baserow/contrib/database/formula/types/typed_field_updater.py index fd60118c0..e56d42630 100644 --- a/backend/src/baserow/contrib/database/formula/types/typed_field_updater.py +++ b/backend/src/baserow/contrib/database/formula/types/typed_field_updater.py @@ -8,6 +8,9 @@ from baserow.contrib.database.fields.registries import ( field_type_registry, field_converter_registry, ) +from baserow.contrib.database.formula.parser.update_field_names import ( + update_field_names, +) from baserow.contrib.database.formula.types.formula_type import ( BaserowFormulaType, ) @@ -51,11 +54,11 @@ def _recreate_field_if_required( def _calculate_and_save_updated_fields( table: "models.Table", - field_id_to_typed_field: Dict[int, TypedFieldWithReferences], + field_name_to_typed_field: Dict[str, TypedFieldWithReferences], field_which_changed=None, ) -> List[Field]: other_changed_fields = {} - for typed_field in field_id_to_typed_field.values(): + for typed_field in field_name_to_typed_field.values(): new_field = typed_field.new_field if not isinstance(new_field, FormulaField): continue @@ -76,7 +79,7 @@ def _calculate_and_save_updated_fields( new_field.save() ViewHandler().field_type_changed(new_field) if not checking_field_which_changed: - other_changed_fields[new_field.id] = new_field + other_changed_fields[new_field.name] = new_field _recreate_field_if_required( table, original_formula_field, formula_field_type, new_field ) @@ -85,9 +88,9 @@ def _calculate_and_save_updated_fields( # All fields that depend on the field_which_changed need to have their # values recalculated as a result, even if their formula or type did not # change as a result. - field_id_to_typed_field[field_which_changed.id].add_all_missing_valid_parents( - other_changed_fields, field_id_to_typed_field - ) + field_name_to_typed_field[ + field_which_changed.name + ].add_all_missing_valid_parents(other_changed_fields, field_name_to_typed_field) return list(other_changed_fields.values()) @@ -101,7 +104,7 @@ class TypedBaserowTableWithUpdatedFields(TypedBaserowTable): def __init__( self, - typed_fields: Dict[int, TypedFieldWithReferences], + typed_fields: Dict[str, TypedFieldWithReferences], table: "models.Table", initially_updated_field: Optional[Field], updated_fields: List[Field], @@ -134,7 +137,7 @@ class TypedBaserowTableWithUpdatedFields(TypedBaserowTable): updated_field, self.model ) if expr is not None: - all_fields_update_dict[f"field_{updated_field.id}"] = expr + all_fields_update_dict[updated_field.db_column] = expr # Also update trash rows so when restored they immediately have correct formula # values. @@ -183,7 +186,7 @@ def type_table_and_update_fields_given_changed_field( ) if isinstance(initial_field, FormulaField): - typed_changed_field = typed_fields[initial_field.id].new_field + typed_changed_field = typed_fields[initial_field.name].new_field else: typed_changed_field = initial_field @@ -195,32 +198,20 @@ def type_table_and_update_fields_given_changed_field( ) -def type_table_and_update_fields_given_deleted_field( - table: "models.Table", deleted_field_id: int, deleted_field_name: str +def update_other_fields_referencing_this_fields_name( + field: "models.Field", new_field_name: str ): - """ - Given a field with the provided name and id has been deleted will retype all formula - fields in the table, update their definitions in the database and return a wrapper - class which can then be used to trigger a recalculation of the changed fields at - an appropriate time. - - Any formulas which reference the deleted field will be changed to have an invalid - type. Those formulas will also have their actual formula changed replacing any - field_by_id references to deleted_field_id with a field reference to the - deleted_field_name. - - :param table: The table from which the field was deleted. - :param deleted_field_id: The id of the field before it was deleted. - :param deleted_field_name: The name of the field before it was deleted. - :return: A wrapper object containing all updated fields and all types for fields in - the table. The updated fields have not yet had their values recalculated as a - result of the field deletion and it is up to you to call - update_values_for_all_updated_fields when appropriate otherwise those fields - will have stale data. - """ - - typed_fields = type_all_fields_in_table( - table, {deleted_field_id: deleted_field_name} - ) - updated_fields = _calculate_and_save_updated_fields(table, typed_fields) - return TypedBaserowTableWithUpdatedFields(typed_fields, table, None, updated_fields) + old_field_name = field.name + field_updates = [] + if old_field_name != new_field_name: + for other_field in field.table.field_set.exclude(id=field.id).all(): + other_field = other_field.specific + if isinstance(other_field, FormulaField): + old_formula = other_field.formula + other_field.formula = update_field_names( + old_formula, {old_field_name: new_field_name} + ) + if old_formula != other_field.formula: + field_updates.append(other_field) + FormulaField.objects.bulk_update(field_updates, fields=["formula"]) + return field_updates diff --git a/backend/src/baserow/contrib/database/formula/types/visitors.py b/backend/src/baserow/contrib/database/formula/types/visitors.py index fbed8ca3a..e33f1be66 100644 --- a/backend/src/baserow/contrib/database/formula/types/visitors.py +++ b/backend/src/baserow/contrib/database/formula/types/visitors.py @@ -5,7 +5,6 @@ from baserow.contrib.database.formula.ast.tree import ( BaserowStringLiteral, BaserowFieldReference, BaserowIntegerLiteral, - BaserowFieldByIdReference, BaserowExpression, BaserowDecimalLiteral, BaserowBooleanLiteral, @@ -26,10 +25,11 @@ from baserow.contrib.database.formula.types import table_typer class FieldReferenceResolvingVisitor(BaserowFormulaASTVisitor[Any, List[str]]): - def visit_field_reference(self, field_reference: BaserowFunctionCall): - # The only time when we should encounter a field reference here is when this - # field is pointing at a trashed or deleted field. - return [] + def visit_field_reference(self, field_reference: BaserowFieldReference): + if field_reference.is_reference_to_valid_field(): + return [field_reference.referenced_field_name] + else: + return [] def visit_string_literal(self, string_literal: BaserowStringLiteral) -> List[str]: return [] @@ -52,16 +52,11 @@ class FieldReferenceResolvingVisitor(BaserowFormulaASTVisitor[Any, List[str]]): def visit_decimal_literal(self, decimal_literal: BaserowDecimalLiteral): return [] - def visit_field_by_id_reference( - self, field_by_id_reference: BaserowFieldByIdReference - ): - return [field_by_id_reference.referenced_field_id] - class FunctionsUsedVisitor( BaserowFormulaASTVisitor[Any, Set[BaserowFunctionDefinition]] ): - def visit_field_reference(self, field_reference: BaserowFunctionCall): + def visit_field_reference(self, field_reference: BaserowFieldReference): return set() def visit_string_literal( @@ -93,26 +88,28 @@ class FunctionsUsedVisitor( ) -> Set[BaserowFunctionDefinition]: return set() - def visit_field_by_id_reference( - self, field_by_id_reference: BaserowFieldByIdReference - ) -> BaserowFunctionDefinition: - return set() - class TypeAnnotatingASTVisitor( BaserowFormulaASTVisitor[UnTyped, BaserowExpression[BaserowFormulaType]] ): def __init__(self, field_id_to_typed_field): - self.field_id_to_typed_field: Dict[ - int, "table_typer.TypedFieldWithReferences" + self.field_to_typed_expr: Dict[ + str, "table_typer.TypedFieldWithReferences" ] = field_id_to_typed_field def visit_field_reference( self, field_reference: BaserowFieldReference[UnTyped] ) -> BaserowExpression[BaserowFormulaType]: - return field_reference.with_invalid_type( - f"references the deleted field {field_reference.referenced_field_name}" - ) + field_name = field_reference.referenced_field_name + if field_name in self.field_to_typed_expr: + updated_typed_field = self.field_to_typed_expr[field_name] + return field_reference.with_type( + updated_typed_field.typed_expression.expression_type + ) + else: + return field_reference.with_invalid_type( + f"references the deleted or unknown field {field_name}" + ) def visit_string_literal( self, string_literal: BaserowStringLiteral[UnTyped] @@ -150,34 +147,23 @@ class TypeAnnotatingASTVisitor( ) -> BaserowExpression[BaserowFormulaType]: return boolean_literal.with_valid_type(BaserowFormulaBooleanType()) - def visit_field_by_id_reference( - self, field_by_id_reference: BaserowFieldByIdReference[UnTyped] - ) -> BaserowExpression[BaserowFormulaType]: - field_id = field_by_id_reference.referenced_field_id - if field_id in self.field_id_to_typed_field: - updated_typed_field = self.field_id_to_typed_field[ - field_by_id_reference.referenced_field_id - ] - return field_by_id_reference.with_type( - updated_typed_field.typed_expression.expression_type - ) - else: - return field_by_id_reference.with_invalid_type( - f"references an unknown field with id " - f"{field_by_id_reference.referenced_field_id}" - ) - -class SubstituteFieldByIdWithThatFieldsExpressionVisitor( +class SubstituteFieldWithThatFieldsExpressionVisitor( BaserowFormulaASTVisitor[Any, BaserowExpression] ): - def visit_field_reference(self, field_reference: BaserowFieldByIdReference): - return field_reference - def __init__( - self, field_id_to_typed_field: Dict[int, "table_typer.TypedFieldWithReferences"] + self, + field_name_to_typed_field: Dict[str, "table_typer.TypedFieldWithReferences"], ): - self.field_id_to_typed_field = field_id_to_typed_field + self.field_name_to_typed_field = field_name_to_typed_field + + def visit_field_reference(self, field_reference: BaserowFieldReference): + field_name = field_reference.referenced_field_name + if field_name in self.field_name_to_typed_field: + typed_field = self.field_name_to_typed_field[field_name] + return typed_field.typed_expression + else: + return field_reference def visit_string_literal( self, string_literal: BaserowStringLiteral @@ -200,14 +186,3 @@ class SubstituteFieldByIdWithThatFieldsExpressionVisitor( def visit_boolean_literal(self, boolean_literal: BaserowBooleanLiteral): return boolean_literal - - def visit_field_by_id_reference( - self, field_by_id_reference: BaserowFieldByIdReference - ) -> BaserowExpression: - if field_by_id_reference.referenced_field_id in self.field_id_to_typed_field: - typed_field = self.field_id_to_typed_field[ - field_by_id_reference.referenced_field_id - ] - return typed_field.typed_expression - else: - return field_by_id_reference diff --git a/backend/src/baserow/contrib/database/migrations/0040_formulafield_remove_field_by_id.py b/backend/src/baserow/contrib/database/migrations/0040_formulafield_remove_field_by_id.py new file mode 100644 index 000000000..bdc21ddb2 --- /dev/null +++ b/backend/src/baserow/contrib/database/migrations/0040_formulafield_remove_field_by_id.py @@ -0,0 +1,209 @@ +# Generated by Django 3.2.6 on 2021-09-15 13:11 + +from typing import Dict, Optional + +from django.db import migrations, models + +from baserow.contrib.database.formula.parser.exceptions import ( + MaximumFormulaSizeError, +) +from baserow.contrib.database.formula.parser.generated.BaserowFormula import ( + BaserowFormula, +) +from baserow.contrib.database.formula.parser.generated.BaserowFormulaVisitor import ( + BaserowFormulaVisitor, +) +from baserow.contrib.database.formula.parser.parser import get_parse_tree_for_formula + + +# Copied from parser.py to ensure future changes to that file dont +# break this migration +def convert_string_literal_token_to_string(string_literal, is_single_q): + literal_without_outer_quotes = string_literal[1:-1] + quote = "'" if is_single_q else '"' + return literal_without_outer_quotes.replace("\\" + quote, quote) + + +# Copied from parser.py to ensure future changes to that file dont +# break this migration +def convert_string_to_string_literal_token(string, is_single_q): + quote = "'" if is_single_q else '"' + escaped = string.replace(quote, "\\" + quote) + return quote + escaped + quote + + +# Copied from update_field_names.py to ensure future changes to that file dont +# break this migration +# noinspection DuplicatedCode +class UpdateFieldNameFormulaVisitor(BaserowFormulaVisitor): + """ + Visits nodes of the BaserowFormula antlr parse tree returning the formula in string + form, but with field(name) and field_by_id(id) references replaced according to the + input dictionaries. + """ + + def __init__( + self, + field_names_to_update: Optional[Dict[str, str]] = None, + field_ids_to_replace_with_name_refs: Optional[Dict[int, str]] = None, + field_names_to_replace_with_id_refs: Optional[Dict[str, int]] = None, + ): + if field_names_to_update is None: + field_names_to_update = {} + if field_ids_to_replace_with_name_refs is None: + field_ids_to_replace_with_name_refs = {} + if field_names_to_replace_with_id_refs is None: + field_names_to_replace_with_id_refs = {} + + self.field_names_to_replace_with_id_refs = field_names_to_replace_with_id_refs + self.field_names_to_update = field_names_to_update + self.field_ids_to_replace_with_name_refs = field_ids_to_replace_with_name_refs + + def visitRoot(self, ctx: BaserowFormula.RootContext): + return ctx.expr().accept(self) + + def visitStringLiteral(self, ctx: BaserowFormula.StringLiteralContext): + return ctx.getText() + + def visitDecimalLiteral(self, ctx: BaserowFormula.DecimalLiteralContext): + return ctx.getText() + + def visitBooleanLiteral(self, ctx: BaserowFormula.BooleanLiteralContext): + return ctx.getText() + + def visitBrackets(self, ctx: BaserowFormula.BracketsContext): + return ctx.expr().accept(self) + + def visitFunctionCall(self, ctx: BaserowFormula.FunctionCallContext): + function_name = ctx.func_name().accept(self).lower() + args = [expr.accept(self) for expr in (ctx.expr())] + args_with_any_field_names_replaced = ",".join(args) + return f"{function_name}({args_with_any_field_names_replaced})" + + def visitBinaryOp(self, ctx: BaserowFormula.BinaryOpContext): + args = [expr.accept(self) for expr in (ctx.expr())] + return args[0] + ctx.op.text + args[1] + + def visitFunc_name(self, ctx: BaserowFormula.Func_nameContext): + return ctx.getText() + + def visitIdentifier(self, ctx: BaserowFormula.IdentifierContext): + return ctx.getText() + + def visitIntegerLiteral(self, ctx: BaserowFormula.IntegerLiteralContext): + return ctx.getText() + + def visitFieldReference(self, ctx: BaserowFormula.FieldReferenceContext): + reference = ctx.field_reference() + is_single_quote_ref = reference.SINGLEQ_STRING_LITERAL() + field_name = convert_string_literal_token_to_string( + reference.getText(), is_single_quote_ref + ) + if field_name in self.field_names_to_update: + new_name = self.field_names_to_update[field_name] + escaped_new_name = convert_string_to_string_literal_token( + new_name, is_single_quote_ref + ) + return f"field({escaped_new_name})" + elif field_name in self.field_names_to_replace_with_id_refs: + return ( + f"field_by_id({self.field_names_to_replace_with_id_refs[field_name]})" + ) + else: + return ctx.getText() + + def visitFieldByIdReference(self, ctx: BaserowFormula.FieldByIdReferenceContext): + field_id = int(str(ctx.INTEGER_LITERAL())) + if field_id not in self.field_ids_to_replace_with_name_refs: + return f"field('unknown field {field_id}')" + new_name = self.field_ids_to_replace_with_name_refs[field_id] + escaped_new_name = convert_string_to_string_literal_token(new_name, True) + return f"field({escaped_new_name})" + + def visitLeftWhitespaceOrComments( + self, ctx: BaserowFormula.LeftWhitespaceOrCommentsContext + ): + updated_expr = ctx.expr().accept(self) + return ctx.ws_or_comment().getText() + updated_expr + + def visitRightWhitespaceOrComments( + self, ctx: BaserowFormula.RightWhitespaceOrCommentsContext + ): + updated_expr = ctx.expr().accept(self) + return updated_expr + ctx.ws_or_comment().getText() + + +def update_field_names( + formula: str, + field_names_to_update: Optional[Dict[str, str]] = None, + field_ids_to_replace_with_name_refs: Optional[Dict[int, str]] = None, + field_names_to_replace_with_id_refs: Optional[Dict[str, int]] = None, +) -> str: + """ + :param formula: The raw formula string to update field names in. + :param field_names_to_update: A dictionary where the keys are the old + field names with the values being the new names to replace the old with. + :param field_ids_to_replace_with_name_refs: To replace field_by_id references + with specific field names then provide this dictionary of field id to name + which will swap field_by_id(key) for a field(value). If a field id is not found + in this dict it will be swapped for field('unknown field {id}'). + :param field_names_to_replace_with_id_refs: To replace field references + with specific field ids then provide this dictionary of field name to id + which will swap field(key) for a field_by_id(value). If a field name is not + found in this dict it will be left alone. + :return: An updated formula string where field and field_by_id references have + been updated accordingly. Whitespace and comments will not have been modified. + """ + + try: + tree = get_parse_tree_for_formula(formula) + return UpdateFieldNameFormulaVisitor( + field_names_to_update, + field_ids_to_replace_with_name_refs, + field_names_to_replace_with_id_refs, + ).visit(tree) + except RecursionError: + raise MaximumFormulaSizeError() + + +# noinspection PyPep8Naming +def forward(apps, schema_editor): + FormulaField = apps.get_model("database", "FormulaField") + + for formula in FormulaField.objects.all(): + field_id_to_name = {} + for field in formula.table.field_set.all(): + field_id_to_name[field.id] = field.name + formula.old_formula_with_field_by_id = formula.formula + formula.formula = update_field_names( + formula.formula, field_ids_to_replace_with_name_refs=field_id_to_name + ) + formula.save() + + +# noinspection PyPep8Naming +def reverse(apps, schema_editor): + FormulaField = apps.get_model("database", "FormulaField") + for formula_field in FormulaField.objects.all(): + field_name_to_id = {} + for field in formula_field.table.field_set.all(): + field_name_to_id[field.name] = field.id + formula_field.formula = update_field_names( + formula_field.formula, field_names_to_replace_with_id_refs=field_name_to_id + ) + formula_field.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("database", "0039_formulafield"), + ] + + operations = [ + migrations.AddField( + model_name="formulafield", + name="old_formula_with_field_by_id", + field=models.TextField(blank=True, null=True), + ), + migrations.RunPython(forward, reverse), + ] diff --git a/backend/src/baserow/contrib/database/table/models.py b/backend/src/baserow/contrib/database/table/models.py index 8292b5dc4..2677f3513 100644 --- a/backend/src/baserow/contrib/database/table/models.py +++ b/backend/src/baserow/contrib/database/table/models.py @@ -454,7 +454,7 @@ class Table( # later which ones are duplicate. duplicate_field_names = [] - already_included_field_ids = set([f.id for f in fields]) + already_included_field_names = set([f.name for f in fields]) # We will have to add each field to with the correct field name and model field # to the attribute list in order for the model to work. @@ -469,7 +469,7 @@ class Table( typed_table = type_table(self) fields += field_type.add_related_fields_to_model( - typed_table, field, already_included_field_ids + typed_table, field, already_included_field_names ) # If attribute_names is True we will not use 'field_{id}' as attribute name, diff --git a/backend/src/baserow/contrib/database/trash/trash_types.py b/backend/src/baserow/contrib/database/trash/trash_types.py index 888f26275..78d6cccbc 100644 --- a/backend/src/baserow/contrib/database/trash/trash_types.py +++ b/backend/src/baserow/contrib/database/trash/trash_types.py @@ -8,7 +8,7 @@ from baserow.contrib.database.fields.registries import field_type_registry from baserow.contrib.database.fields.signals import field_restored from baserow.contrib.database.formula.types.typed_field_updater import ( type_table_and_update_fields_given_changed_field, - type_table_and_update_fields_given_deleted_field, + type_table_and_update_fields, ) from baserow.contrib.database.rows.signals import row_created from baserow.contrib.database.table.models import Table, GeneratedTableModel @@ -135,10 +135,8 @@ class FieldTrashableItemType(TrashableItemType): schema_editor.remove_field(from_model, model_field) table = field.table - field_id = field.id - field_name = field.name field.delete() - type_table_and_update_fields_given_deleted_field(table, field_id, field_name) + type_table_and_update_fields(table) # After the field is deleted we are going to to call the after_delete method of # the field type because some instance cleanup might need to happen. diff --git a/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py b/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py index 260c34bbf..9b76a97e8 100644 --- a/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py +++ b/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py @@ -225,7 +225,7 @@ def test_trashing_child_field(api_client, data_fixture): assert len(response_json["related_fields"]) == 1 assert response_json["related_fields"][0]["id"] == formula_field_id assert ( - "references the deleted field number" + "references the deleted or unknown field number" in response_json["related_fields"][0]["error"] ) @@ -236,7 +236,7 @@ def test_trashing_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - assert "references the deleted field number" in response_json[0]["error"] + assert "references the deleted or unknown field number" in response_json[0]["error"] @pytest.mark.django_db @@ -276,7 +276,7 @@ def test_perm_deleting_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - assert "references the deleted field number" in response_json[0]["error"] + assert "references the deleted or unknown field number" in response_json[0]["error"] @pytest.mark.django_db @@ -321,7 +321,7 @@ def test_trashing_restoring_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - assert "references the deleted field number" in response_json[0]["error"] + assert "references the deleted or unknown field number" in response_json[0]["error"] assert response_json[0]["formula"] == "field('number')+1" response = api_client.patch( @@ -343,7 +343,7 @@ def test_trashing_restoring_child_field(api_client, data_fixture): response_json = response.json() assert response.status_code == HTTP_200_OK, response_json assert response_json[1]["error"] is None - assert response_json[1]["formula"] == f"field_by_id({fields[0].id})+1" + assert response_json[1]["formula"] == f"field('{fields[0].name}')+1" response = api_client.get( reverse("api:database:rows:list", kwargs={"table_id": table.id}), @@ -398,7 +398,7 @@ def test_trashing_renaming_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - assert "references the deleted field number" in response_json[1]["error"] + assert "references the deleted or unknown field number" in response_json[1]["error"] assert response_json[1]["formula"] == "field('number')+1" # We rename the other field to fit into the formula slot @@ -418,7 +418,7 @@ def test_trashing_renaming_child_field(api_client, data_fixture): response_json = response.json() assert response.status_code == HTTP_200_OK, response_json assert response_json[1]["error"] is None - assert response_json[1]["formula"] == f"field_by_id({fields[1].id})+1" + assert response_json[1]["formula"] == f"field('number')+1" response = api_client.get( reverse("api:database:rows:list", kwargs={"table_id": table.id}), @@ -473,7 +473,7 @@ def test_trashing_creating_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - assert "references the deleted field number" in response_json[0]["error"] + assert "references the deleted or unknown field number" in response_json[0]["error"] assert response_json[0]["formula"] == "field('number')+1" # We create the another field to fit into the formula slot @@ -485,7 +485,6 @@ def test_trashing_creating_child_field(api_client, data_fixture): ) response_json = response.json() assert response.status_code == HTTP_200_OK, response_json - new_field_id = response_json["id"] response = api_client.get( reverse("api:database:fields:item", kwargs={"field_id": formula_field_id}), @@ -495,7 +494,7 @@ def test_trashing_creating_child_field(api_client, data_fixture): response_json = response.json() assert response.status_code == HTTP_200_OK, response_json assert response_json["error"] is None - assert response_json["formula"] == f"field_by_id({new_field_id})+1" + assert response_json["formula"] == f"field('number')+1" response = api_client.get( reverse("api:database:rows:list", kwargs={"table_id": table.id}), diff --git a/backend/tests/baserow/contrib/database/field/test_field_types.py b/backend/tests/baserow/contrib/database/field/test_field_types.py index 2c320022f..674fb89bf 100644 --- a/backend/tests/baserow/contrib/database/field/test_field_types.py +++ b/backend/tests/baserow/contrib/database/field/test_field_types.py @@ -51,7 +51,7 @@ def test_import_export_formula_field(data_fixture, api_client): formula_field = data_fixture.create_formula_field( table=first_table, name="formula field", - formula=f"field_by_id({text_field.id})", + formula=f"field('{text_field.name}')", formula_type="text", ) formula_field_type = field_type_registry.get_by_model(formula_field) diff --git a/backend/tests/baserow/contrib/database/field/test_formula_field_type.py b/backend/tests/baserow/contrib/database/field/test_formula_field_type.py index 354628547..4a3c2d58f 100644 --- a/backend/tests/baserow/contrib/database/field/test_formula_field_type.py +++ b/backend/tests/baserow/contrib/database/field/test_formula_field_type.py @@ -2,9 +2,6 @@ import pytest from baserow.contrib.database.fields.handler import FieldHandler from baserow.contrib.database.fields.registries import field_type_registry -from baserow.contrib.database.formula.parser.ast_mapper import ( - replace_field_refs_according_to_new_or_deleted_fields, -) from baserow.contrib.database.formula.types.formula_type import ( BaserowFormulaInvalidType, ) @@ -115,32 +112,6 @@ def test_get_set_export_serialized_value_formula_field(data_fixture): assert old_row_2_value == getattr(row_2, formula_field_name) -@pytest.mark.django_db -def test_can_replace_field_with_field_by_id_whilst_keeping_whitespace(data_fixture): - assert ( - replace_field_refs_according_to_new_or_deleted_fields( - 'field\n(\n"My Field Name")', {}, {"My Field Name": 1} - ) - == "field_by_id\n(\n1)" - ) - assert ( - replace_field_refs_according_to_new_or_deleted_fields( - """concat(\nfield_by_id(2), 'test')""", - {2: "Deleted Field"}, - {"My Field Name": 3}, - ) - == """concat(\nfield('Deleted Field'), 'test')""" - ) - assert ( - replace_field_refs_according_to_new_or_deleted_fields( - """concat(\nfield('Deleted Field'), 'test')""", - {2: "Deleted Field"}, - {"My Field Name": 3}, - ) - == """concat(\nfield('Deleted Field'), 'test')""" - ) - - @pytest.mark.django_db def test_changing_type_of_other_field_still_results_in_working_filter(data_fixture): user = data_fixture.create_user() @@ -274,3 +245,26 @@ def test_formula_with_row_id_is_populated_after_creating_row( row = RowHandler().create_row(user=user, table=table) assert getattr(row, f"field_{formula_field.id}") == row.id + + +@pytest.mark.django_db +def test_can_rename_field_preserving_whitespace( + data_fixture, +): + user = data_fixture.create_user() + table = data_fixture.create_database_table(user=user) + handler = FieldHandler() + test_field = handler.create_field( + user=user, table=table, type_name="text", name="a" + ) + formula_field = handler.create_field( + user=user, table=table, type_name="formula", name="2", formula=" field('a') \n" + ) + + assert formula_field.formula == f" field('a') \n" + + handler.update_field(user=user, field=test_field, name="b") + + formula_field.refresh_from_db() + + assert formula_field.formula == f" field('b') \n" diff --git a/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py b/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py index 9be9b328d..f6eb862da 100644 --- a/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py +++ b/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py @@ -312,6 +312,7 @@ INVALID_FORMULA_TESTS = [ ("10/LOWER(1)", "ERROR_WITH_FORMULA", None), ("'t'/1", "ERROR_WITH_FORMULA", None), ("1/'t'", "ERROR_WITH_FORMULA", None), + ("field(9999)", "ERROR_WITH_FORMULA", None), ("field_by_id(9999)", "ERROR_WITH_FORMULA", None), ( "upper(1)", diff --git a/backend/tests/baserow/contrib/database/formula/test_replace_field_by_id_with_field.py b/backend/tests/baserow/contrib/database/formula/test_replace_field_by_id_with_field.py deleted file mode 100644 index ec17feb43..000000000 --- a/backend/tests/baserow/contrib/database/formula/test_replace_field_by_id_with_field.py +++ /dev/null @@ -1,76 +0,0 @@ -from baserow.contrib.database.formula.parser.replace_field_by_id_with_field import ( - replace_field_by_id_with_field, -) - - -def test_replace_single_field_by_id(): - new_formula = replace_field_by_id_with_field("field_by_id(1)", {1: "newName"}) - - assert new_formula == "field('newName')" - - -def test_replace_field_by_id_keeping_whitespace(): - new_formula = replace_field_by_id_with_field( - "field_by_id( \n \n1 )", {1: "newName"} - ) - - assert new_formula == "field( \n \n'newName' )" - - -def test_can_replace_field_by_id_keeping_whitespace_and_comments(): - new_formula = replace_field_by_id_with_field( - "/* comment */field_by_id(/* comment */ \n \n1 /* a comment */)", - {1: "newName"}, - ) - - assert ( - new_formula - == "/* comment */field(/* comment */ \n \n'newName' /* a comment */)" - ) - - -def test_replace_field_by_id_with_a_name_containing_double_quotes(): - new_formula = replace_field_by_id_with_field("field_by_id(1)", {1: 'name with "'}) - - assert new_formula == "field('name with \"')" - - -def test_can_replace_multiple_different_ids(): - new_formula = replace_field_by_id_with_field( - "concat(field_by_id(1), field_by_id(1), field_by_id(2))", - {1: "newName", 2: "newOther"}, - ) - - assert ( - new_formula == "concat(field('newName'), field('newName'), field('newOther'))" - ) - - -def test_doesnt_change_field_by_id_not_in_dict(): - new_formula = replace_field_by_id_with_field( - "field_by_id(2)", - { - 1: "newName", - }, - ) - - assert new_formula == "field_by_id(2)" - - -def test_returns_same_formula_for_invalid_syntax(): - _assert_returns_same("field_by_id(2") - _assert_returns_same("field_by_id('test')") - _assert_returns_same("field_by_id(test)") - _assert_returns_same("field_by_id((test))") - _assert_returns_same("field_by_id('''test'')") - _assert_returns_same( - "field_by_id(111111111111111111111111111111111111111111111111111111111111111)" - ) - - -def _assert_returns_same(formula): - new_formula = replace_field_by_id_with_field( - formula, - {2: "newName"}, - ) - assert new_formula == formula diff --git a/backend/tests/baserow/contrib/database/formula/test_replace_field_with_field_by_id.py b/backend/tests/baserow/contrib/database/formula/test_replace_field_with_field_by_id.py deleted file mode 100644 index d08925d11..000000000 --- a/backend/tests/baserow/contrib/database/formula/test_replace_field_with_field_by_id.py +++ /dev/null @@ -1,100 +0,0 @@ -from baserow.contrib.database.formula.parser.replace_field_with_field_by_id import ( - replace_field_with_field_by_id, -) - - -def test_replace_single_quoted_field_ref_with_id(): - new_formula = replace_field_with_field_by_id("field('test')", {"test": 1}) - - assert new_formula == "field_by_id(1)" - - -def test_replace_double_quoted_field_ref_with_id(): - new_formula = replace_field_with_field_by_id('field("test")', {"test": 1}) - - assert new_formula == "field_by_id(1)" - - -def test_replace_field_reference_keeping_whitespace(): - new_formula = replace_field_with_field_by_id("field( \n \n'test' )", {"test": 1}) - - assert new_formula == "field_by_id( \n \n1 )" - - -def test_replace_double_quote_field_ref_containing_single_quotes(): - new_formula = replace_field_with_field_by_id( - 'field("test with \'")', {"test with '": 1} - ) - - assert new_formula == "field_by_id(1)" - - -def test_replace_double_quote_field_ref_containing_double_quotes(): - new_formula = replace_field_with_field_by_id( - 'field("test with \\"")', {'test with "': 1} - ) - - assert new_formula == "field_by_id(1)" - - -def test_replace_single_quote_field_ref_containing_single_quotes(): - new_formula = replace_field_with_field_by_id( - "field('test with \\'')", {"test with '": 1} - ) - - assert new_formula == "field_by_id(1)" - - -def test_replace_single_quote_field_ref_containing_double_quotes(): - new_formula = replace_field_with_field_by_id( - "field('test with \"')", {'test with "': 1} - ) - - assert new_formula == "field_by_id(1)" - - -def test_can_replace_field_ref_keeping_whitespace_and_comments(): - new_formula = replace_field_with_field_by_id( - "/* comment */field(/* comment */ \n \n'test' /* a comment */)", - {"test": 1}, - ) - - assert ( - new_formula == "/* comment */field_by_id(/* comment */ \n \n1 /* a comment */)" - ) - - -def test_can_replace_multiple_different_field_references(): - new_formula = replace_field_with_field_by_id( - 'concat(field("test"), field("test"), field(\'other\'))', - {"test": 1, "other": 2}, - ) - - assert new_formula == "concat(field_by_id(1), field_by_id(1), field_by_id(2))" - - -def test_leaves_unknown_field_references_along(): - new_formula = replace_field_with_field_by_id( - "field('test')", - {"notTest": 1}, - ) - assert new_formula == "field('test')" - - -def test_returns_same_formula_with_field_names_for_invalid_syntax(): - _assert_returns_same("field('test'") - _assert_returns_same("field(''''test'") - _assert_returns_same("field(test") - _assert_returns_same("field(1)") - _assert_returns_same("field)") - _assert_returns_same("field_by_id(1)") - - -def _assert_returns_same(formula): - new_formula = replace_field_with_field_by_id( - formula, - { - "test": 1, - }, - ) - assert new_formula == formula diff --git a/backend/tests/baserow/contrib/database/formula/test_update_field_names.py b/backend/tests/baserow/contrib/database/formula/test_update_field_names.py new file mode 100644 index 000000000..e8d5ecf45 --- /dev/null +++ b/backend/tests/baserow/contrib/database/formula/test_update_field_names.py @@ -0,0 +1,207 @@ +import pytest + +from baserow.contrib.database.formula.parser.exceptions import BaserowFormulaSyntaxError +from baserow.contrib.database.formula.parser.update_field_names import ( + update_field_names, +) + + +def test_replace_single_quoted_field_ref(): + new_formula = update_field_names("field('test')", {"test": "new test"}) + + assert new_formula == "field('new test')" + + +def test_replace_double_quoted_field_ref(): + new_formula = update_field_names('field("test")', {"test": "new test"}) + + assert new_formula == 'field("new test")' + + +def test_replace_field_reference_keeping_whitespace(): + new_formula = update_field_names(" \n\tfield('test') \n\t", {"test": "new test"}) + + assert new_formula == " \n\tfield('new test') \n\t" + + +def test_replace_field_reference_keeping_whitespace_and_comments(): + new_formula = update_field_names( + "//my line comment \n\tfield('test') /*my block comment*/\n\t", + {"test": "new " "test"}, + ) + + assert ( + new_formula == "//my line comment \n\tfield('new test') /*my block " + "comment*/\n\t" + ) + + +def test_replace_field_reference_preserving_case(): + new_formula = update_field_names( + "//my line comment \n\tADD(fIeLd('test'),1) /*my block comment*/\n\t", + {"test": "new " "test"}, + ) + + assert ( + new_formula == "//my line comment \n\tADD(fIeLd('new test'),1) /*my block " + "comment*/\n\t" + ) + + +def test_replace_binary_op_keeping_whitespace_and_comments(): + new_formula = update_field_names( + "//my line comment \n\t1+1 /*my block comment*/\n\t", + {"test": "new " "test"}, + ) + + assert new_formula == "//my line comment \n\t1+1 /*my block " "comment*/\n\t" + + +def test_replace_function_call_keeping_whitespace_and_comments(): + new_formula = update_field_names( + "//my line comment \n\tadd( 1\t \t+\t \t1,\nfield('test')\t) /*my block " + "comment*/\n\t", + {"test": "new test"}, + ) + + assert ( + new_formula == "//my line comment \n\tadd( 1\t \t+\t \t1,\nfield('new " + "test')\t) /*my block comment*/\n\t" + ) + + +def test_replace_double_quote_field_ref_containing_single_quotes(): + new_formula = update_field_names( + 'field("test with \'")', {"test with '": "new test with ' \\' and \" and \\\""} + ) + + assert new_formula == 'field("new test with \' \\\' and \\" and \\\\"")' + + +def test_replace_double_quote_field_ref_containing_double_quotes(): + new_formula = update_field_names( + "field('test with \\'')", {"test with '": "new test with ' \\' and \" and \\\""} + ) + + assert new_formula == "field('new test with \\' \\\\' and \" and \\\"')" + + +def test_can_replace_multiple_different_field_references(): + new_formula = update_field_names( + 'concat(field("test"), field("test"), field(\'other\'))', + {"test": "new test", "other": "new other"}, + ) + + assert ( + new_formula == 'concat(field("new test"), field("new test"), ' + "field('new other'))" + ) + + +def test_leaves_unknown_field_references_along(): + new_formula = update_field_names( + "field('test')", + {}, + ) + assert new_formula == "field('test')" + + +def test_raises_with_field_names_for_invalid_syntax(): + _assert_raises("field('test'") + _assert_raises("field(''''test'") + _assert_raises("field(test") + _assert_raises("field(1)") + _assert_raises("field)") + + +def _assert_raises(formula): + with pytest.raises(BaserowFormulaSyntaxError): + update_field_names( + formula, + { + "test": "new test", + }, + ) + + +def test_replaces_unknown_field_by_id_with_field(): + new_formula = update_field_names( + "field_by_id(1)", + {}, + ) + assert new_formula == "field('unknown field 1')" + + +def test_replaces_unknown_field_by_id_with_field_multiple(): + new_formula = update_field_names( + "field_by_id(1)+concat(field('a'), field_by_id(2))", + {}, + ) + assert ( + new_formula == "field('unknown field 1')+concat(field('a'), field('unknown " + "field 2'))" + ) + + +def test_replaces_known_field_by_id(): + new_formula = update_field_names( + "field_by_id(1)+concat(field('a'), field_by_id(2))", + field_ids_to_replace_with_name_refs={1: "test", 2: "other_test"}, + ) + assert new_formula == "field('test')+concat(field('a'), field('other_test'))" + + +def test_replaces_functions_preserving_case(): + new_formula = update_field_names( + "field_by_id(1)+CONCAT(field('a'), field_by_id(2))", + field_ids_to_replace_with_name_refs={1: "test", 2: "other_test"}, + ) + assert new_formula == "field('test')+CONCAT(field('a'), field('other_test'))" + + +def test_replaces_known_field_by_id_single_quotes(): + new_formula = update_field_names( + "field_by_id(1)", + field_ids_to_replace_with_name_refs={1: "test with ' '", 2: "other_test"}, + ) + assert new_formula == "field('test with \\' \\'')" + + +def test_replaces_known_field_by_id_double_quotes(): + new_formula = update_field_names( + "field_by_id(1)", + field_ids_to_replace_with_name_refs={1: 'test with " "', 2: "other_test"}, + ) + assert new_formula == "field('test with \" \"')" + + +def test_replaces_field_with_field_by_id(): + new_formula = update_field_names( + "field('a')", + field_names_to_replace_with_id_refs={"a": 1}, + ) + assert new_formula == "field_by_id(1)" + + +def test_doesnt_replace_unknown_field(): + new_formula = update_field_names( + "field('b')+concat(field('a'), field('c'))", + field_names_to_replace_with_id_refs={"a": 1}, + ) + assert new_formula == "field('b')+concat(field_by_id(1), field('c'))" + + +def test_replaces_field_with_single_quotes_with_id(): + new_formula = update_field_names( + "field('test with \\' \\'')", + field_names_to_replace_with_id_refs={"test with ' '": 1, "other_test": 2}, + ) + assert new_formula == "field_by_id(1)" + + +def test_replaces_field_with_double_quotes_with_id(): + new_formula = update_field_names( + "field('test with \" \"')", + field_names_to_replace_with_id_refs={'test with " "': 1, "other_test": 2}, + ) + assert new_formula == "field_by_id(1)" diff --git a/backend/tests/baserow/contrib/database/migrations/test_remove_field_by_id_migration.py b/backend/tests/baserow/contrib/database/migrations/test_remove_field_by_id_migration.py new file mode 100644 index 000000000..7763098e7 --- /dev/null +++ b/backend/tests/baserow/contrib/database/migrations/test_remove_field_by_id_migration.py @@ -0,0 +1,106 @@ +import pytest +from django.core.management import call_command +from django.db import DEFAULT_DB_ALIAS + +# noinspection PyPep8Naming +from django.db import connection +from django.db.migrations.executor import MigrationExecutor + + +# noinspection PyPep8Naming +@pytest.mark.django_db +def test_forwards_migration(data_fixture, transactional_db): + migrate_from = [("database", "0039_formulafield")] + migrate_to = [("database", "0040_formulafield_remove_field_by_id")] + + old_state = migrate(migrate_from) + + # The models used by the data_fixture below are not touched by this migration so + # it is safe to use the latest version in the test. + user = data_fixture.create_user() + table = data_fixture.create_database_table(user=user) + text_field = data_fixture.create_text_field(user=user, table=table, name="text") + FormulaField = old_state.apps.get_model("database", "FormulaField") + ContentType = old_state.apps.get_model("contenttypes", "ContentType") + content_type_id = ContentType.objects.get_for_model(FormulaField).id + formula_field = FormulaField.objects.create( + table_id=table.id, + formula_type="text", + formula=f"field_by_id({text_field.id})", + content_type_id=content_type_id, + order=0, + ) + unknown_field_by_id = FormulaField.objects.create( + table_id=table.id, + formula_type="text", + formula=f"field_by_id(9999)", + content_type_id=content_type_id, + order=0, + ) + + new_state = migrate(migrate_to) + NewFormulaField = new_state.apps.get_model("database", "FormulaField") + + new_formula_field = NewFormulaField.objects.get(id=formula_field.id) + assert new_formula_field.formula == "field('text')" + assert ( + new_formula_field.old_formula_with_field_by_id + == f"field_by_id({text_field.id})" + ) + new_unknown_field_by_id = NewFormulaField.objects.get(id=unknown_field_by_id.id) + assert new_unknown_field_by_id.formula == "field('unknown field 9999')" + assert new_unknown_field_by_id.old_formula_with_field_by_id == f"field_by_id(9999)" + + # We need to apply the latest migration otherwise other tests might fail. + call_command("migrate", verbosity=0, database=DEFAULT_DB_ALIAS) + + +# noinspection PyPep8Naming +@pytest.mark.django_db +def test_backwards_migration(data_fixture, transactional_db): + migrate_from = [("database", "0040_formulafield_remove_field_by_id")] + migrate_to = [("database", "0039_formulafield")] + + old_state = migrate(migrate_from) + + # The models used by the data_fixture below are not touched by this migration so + # it is safe to use the latest version in the test. + user = data_fixture.create_user() + table = data_fixture.create_database_table(user=user) + text_field = data_fixture.create_text_field(user=user, table=table, name="text") + FormulaField = old_state.apps.get_model("database", "FormulaField") + ContentType = old_state.apps.get_model("contenttypes", "ContentType") + content_type_id = ContentType.objects.get_for_model(FormulaField).id + formula_field = FormulaField.objects.create( + table_id=table.id, + formula_type="text", + formula=f"field('text')", + content_type_id=content_type_id, + order=0, + ) + unknown_field = FormulaField.objects.create( + table_id=table.id, + formula_type="text", + formula=f"field('unknown')", + content_type_id=content_type_id, + order=0, + ) + + new_state = migrate(migrate_to) + NewFormulaField = new_state.apps.get_model("database", "FormulaField") + + new_formula_field = NewFormulaField.objects.get(id=formula_field.id) + assert new_formula_field.formula == f"field_by_id({text_field.id})" + new_unknown_field_by_id = NewFormulaField.objects.get(id=unknown_field.id) + assert new_unknown_field_by_id.formula == "field('unknown')" + + # We need to apply the latest migration otherwise other tests might fail. + call_command("migrate", verbosity=0, database=DEFAULT_DB_ALIAS) + + +def migrate(target): + executor = MigrationExecutor(connection) + executor.loader.build_graph() # reload. + executor.migrate(target) + new_state = executor.loader.project_state(target) + return new_state diff --git a/backend/tests/baserow/contrib/database/table/test_table_models.py b/backend/tests/baserow/contrib/database/table/test_table_models.py index ccac08dfe..b45db36eb 100644 --- a/backend/tests/baserow/contrib/database/table/test_table_models.py +++ b/backend/tests/baserow/contrib/database/table/test_table_models.py @@ -676,7 +676,7 @@ def test_table_model_fields_requiring_refresh_on_insert(data_fixture): field_needing_refresh = data_fixture.create_formula_field( table=table, - name="Formula", + name="Formula2", formula="row_id()", formula_type="number", number_decimal_places=0, diff --git a/backend/tests/baserow/contrib/database/test_database_application_type.py b/backend/tests/baserow/contrib/database/test_database_application_type.py index 0cbb9f668..f20c9c7ff 100644 --- a/backend/tests/baserow/contrib/database/test_database_application_type.py +++ b/backend/tests/baserow/contrib/database/test_database_application_type.py @@ -12,7 +12,7 @@ def test_import_export_database(data_fixture): formula_field = data_fixture.create_formula_field( table=table, name="formula", - formula=f"field_by_id({text_field.id})", + formula=f"field('{text_field.name}')", formula_type="text", ) view = data_fixture.create_grid_view(table=table) @@ -52,7 +52,7 @@ def test_import_export_database(data_fixture): imported_text_field = TextField.objects.get(table=imported_table) imported_formula_field = FormulaField.objects.get(table=imported_table) - assert imported_formula_field.formula == f"field_by_id({imported_text_field.id})" + assert imported_formula_field.formula == f"field('{imported_text_field.name}')" # Because the rows have unique id within the table, we expect the row id to be the # same. diff --git a/changelog.md b/changelog.md index 637656040..1c72c1d1b 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ * Added a licensing system for the premium version. * Fixed bug where it was possible to create duplicate trash entries. * Fixed propType validation error when converting from a date field to a boolean field. +* Deprecate internal formula field function field_by_id. ## Released (2021-10-05) diff --git a/docs/guides/formula-technical-guide.md b/docs/guides/formula-technical-guide.md index de2d2da17..fff75256b 100644 --- a/docs/guides/formula-technical-guide.md +++ b/docs/guides/formula-technical-guide.md @@ -137,22 +137,13 @@ error rather than always returning false or something. Renaming a field will rename any references to that field in a formula. This is achieved by the following process: -* Transforming all formulas stored in the backend so that a `field('name')` - reference is replaced with a `field_by_id(1234)` reference where the number is the id - of the field. -* This way internally a formula directly references fields by id and not by name. -* So renaming a field does not break any formulas as the id remains the same. -* The frontend then dynamically does the reverse transformation replacing `field_by_id` - references with `field` for the user to see and edit in realtime. +* Updating all formulas referencing that field to reference the new name +* Returning these updated fields to the browser. -Sometimes we do not do the backend transformation or reverse it. For example: - -* If a field is deleted we transform any references to it back to the `field('name')` - format. +When deleting a field we: * Mark the formulas which referenced it as broken with an error. * This then lets the user create a new field called `'name'` which will then fix those - broken formulas and be substituted in with that new field's id as a - `field_by_id`. + broken formulas. This also can happen when a field is restored from deletion or a field is renamed. diff --git a/formula/BaserowFormula.g4 b/formula/BaserowFormula.g4 index 6d086b9fc..28cadf20d 100644 --- a/formula/BaserowFormula.g4 +++ b/formula/BaserowFormula.g4 @@ -34,16 +34,26 @@ expr | INTEGER_LITERAL # IntegerLiteral | NUMERIC_LITERAL # DecimalLiteral | (TRUE | FALSE) # BooleanLiteral + | ws_or_comment expr # LeftWhitespaceOrComments + | expr ws_or_comment # RightWhitespaceOrComments | OPEN_PAREN expr CLOSE_PAREN # Brackets | expr op=(SLASH | STAR) expr # BinaryOp | expr op=(PLUS | MINUS) expr # BinaryOp | expr op=(GT | LT | GTE | LTE) expr # BinaryOp | expr op=(EQUAL | BANG_EQUAL) expr # BinaryOp | FIELD OPEN_PAREN field_reference CLOSE_PAREN # FieldReference + // FIELDBYID has been depricated and should not be used, it is only included here + // for backwards compatability. | FIELDBYID OPEN_PAREN INTEGER_LITERAL CLOSE_PAREN # FieldByIdReference | func_name OPEN_PAREN (expr (COMMA expr)*)? CLOSE_PAREN # FunctionCall ; +ws_or_comment + : BLOCK_COMMENT + | LINE_COMMENT + | WHITESPACE + ; + func_name : identifier ; diff --git a/formula/BaserowFormula.tokens b/formula/BaserowFormula.tokens index 805982389..1b6673037 100644 --- a/formula/BaserowFormula.tokens +++ b/formula/BaserowFormula.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/formula/BaserowFormulaLexer.g4 b/formula/BaserowFormulaLexer.g4 index e1f3feacb..c7ad1754d 100644 --- a/formula/BaserowFormulaLexer.g4 +++ b/formula/BaserowFormulaLexer.g4 @@ -22,10 +22,6 @@ lexer grammar BaserowFormulaLexer; -// Skip whitespace and comments -WHITESPACE : [ \t\r\n]+ -> channel(HIDDEN); -BLOCK_COMMENT : '/*' .*? '*/' -> channel(HIDDEN); -LINE_COMMENT : '//' .*? '\n' -> channel(HIDDEN); // Fragments fragment A : ('A'|'a') ; @@ -62,6 +58,11 @@ fragment DQUOTA_STRING : '"' ( '\\'. | ~('"' | '\\') )* '"'; fragment SQUOTA_STRING : '\'' ('\\'. | ~('\'' | '\\'))* '\''; fragment BQUOTA_STRING : '`' ( '\\'. | '``' | ~('`' | '\\'))* '`'; +// Skip whitespace and comments +BLOCK_COMMENT : '/*' .* '*/'; +LINE_COMMENT : '//' ~[\r\n]*; +WHITESPACE : [ \t\r\n]+; + TRUE : T R U E; FALSE : F A L S E; diff --git a/formula/BaserowFormulaLexer.tokens b/formula/BaserowFormulaLexer.tokens index 805982389..1b6673037 100644 --- a/formula/BaserowFormulaLexer.tokens +++ b/formula/BaserowFormulaLexer.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/web-frontend/modules/database/components/field/FieldFormulaSubForm.vue b/web-frontend/modules/database/components/field/FieldFormulaSubForm.vue index 2a3dfe7b3..26fbef486 100644 --- a/web-frontend/modules/database/components/field/FieldFormulaSubForm.vue +++ b/web-frontend/modules/database/components/field/FieldFormulaSubForm.vue @@ -38,8 +38,6 @@ import FieldFormulaInitialSubForm from '@baserow/modules/database/components/for import FormulaAdvancedEditContext from '@baserow/modules/database/components/formula/FormulaAdvancedEditContext' import FormulaService from '@baserow/modules/database/services/formula' import parseBaserowFormula from '@baserow/modules/database/formula/parser/parser' -import { replaceFieldByIdWithField } from '@baserow/modules/database/formula/parser/replaceFieldByIdWithField' -import { updateFieldNames } from '@baserow/modules/database/formula/parser/updateFieldNames' import { FileFieldType, LinkRowFieldType, @@ -90,12 +88,6 @@ export default { return isNotThisField && !isInvalidFieldType }) }, - fieldIdToNameMap() { - return this.rawFields.reduce(function (map, obj) { - map[obj.id] = obj.name - return map - }, {}) - }, localOrServerError() { const dirty = this.$v.values.formula.$dirty if (dirty && !this.$v.values.formula.required) { @@ -133,20 +125,7 @@ export default { }, }, watch: { - fieldIdToNameMap(idToNewNames, idToOldNames) { - const oldToNewNameMapBuilder = function (map, key) { - map[idToOldNames[key]] = idToNewNames[key] - return map - } - const oldKnownFieldIds = Object.keys(idToOldNames) - const oldFieldNameToNewFieldName = oldKnownFieldIds.reduce( - oldToNewNameMapBuilder, - {} - ) - this.fieldNameChanged(oldFieldNameToNewFieldName) - }, defaultValues(newValue, oldValue) { - this.convertServerSideFormulaToClient(newValue.formula) this.mergedTypeOptions = Object.assign({}, newValue) }, 'values.formula'(newValue, oldValue) { @@ -163,7 +142,9 @@ export default { } try { parseBaserowFormula(value) - this.convertServerSideFormulaToClient(value) + if (!this.initialFormula) { + this.initialFormula = this.values.formula + } this.parsingError = null return true } catch (e) { @@ -171,26 +152,6 @@ export default { return false } }, - convertServerSideFormulaToClient(formula) { - if (formula != null) { - this.values.formula = replaceFieldByIdWithField( - formula, - this.fieldIdToNameMap - ) - if (!this.initialFormula) { - this.initialFormula = this.values.formula - } - } - }, - fieldNameChanged(oldNameToNewNameMap) { - this.convertServerSideFormulaToClient(this.values.formula) - if (this.values.formula != null) { - this.values.formula = updateFieldNames( - this.values.formula, - oldNameToNewNameMap - ) - } - }, toHumanReadableErrorMessage(error) { const s = error.message .replace('extraneous', 'Invalid') diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.interp b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.interp index 5b27890b7..442cad054 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.interp +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.interp @@ -85,9 +85,9 @@ null token symbolic names: null -WHITESPACE BLOCK_COMMENT LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -171,10 +171,11 @@ ErrorCharacter rule names: root expr +ws_or_comment func_name field_reference identifier atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 84, 74, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 40, 10, 3, 12, 3, 14, 3, 43, 11, 3, 5, 3, 45, 10, 3, 3, 3, 3, 3, 5, 3, 49, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 63, 10, 3, 12, 3, 14, 3, 66, 11, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 2, 3, 4, 7, 2, 4, 6, 8, 10, 2, 9, 3, 2, 6, 7, 4, 2, 15, 15, 74, 74, 4, 2, 62, 62, 68, 68, 4, 2, 42, 43, 53, 54, 4, 2, 38, 38, 40, 40, 3, 2, 26, 27, 3, 2, 28, 29, 2, 82, 2, 12, 3, 2, 2, 2, 4, 48, 3, 2, 2, 2, 6, 67, 3, 2, 2, 2, 8, 69, 3, 2, 2, 2, 10, 71, 3, 2, 2, 2, 12, 13, 5, 4, 3, 2, 13, 14, 7, 2, 2, 3, 14, 3, 3, 2, 2, 2, 15, 16, 8, 3, 1, 2, 16, 49, 7, 26, 2, 2, 17, 49, 7, 27, 2, 2, 18, 49, 7, 23, 2, 2, 19, 49, 7, 22, 2, 2, 20, 49, 9, 2, 2, 2, 21, 22, 7, 16, 2, 2, 22, 23, 5, 4, 3, 2, 23, 24, 7, 17, 2, 2, 24, 49, 3, 2, 2, 2, 25, 26, 7, 8, 2, 2, 26, 27, 7, 16, 2, 2, 27, 28, 5, 8, 5, 2, 28, 29, 7, 17, 2, 2, 29, 49, 3, 2, 2, 2, 30, 31, 7, 9, 2, 2, 31, 32, 7, 16, 2, 2, 32, 33, 7, 23, 2, 2, 33, 49, 7, 17, 2, 2, 34, 35, 5, 6, 4, 2, 35, 44, 7, 16, 2, 2, 36, 41, 5, 4, 3, 2, 37, 38, 7, 10, 2, 2, 38, 40, 5, 4, 3, 2, 39, 37, 3, 2, 2, 2, 40, 43, 3, 2, 2, 2, 41, 39, 3, 2, 2, 2, 41, 42, 3, 2, 2, 2, 42, 45, 3, 2, 2, 2, 43, 41, 3, 2, 2, 2, 44, 36, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 46, 3, 2, 2, 2, 46, 47, 7, 17, 2, 2, 47, 49, 3, 2, 2, 2, 48, 15, 3, 2, 2, 2, 48, 17, 3, 2, 2, 2, 48, 18, 3, 2, 2, 2, 48, 19, 3, 2, 2, 2, 48, 20, 3, 2, 2, 2, 48, 21, 3, 2, 2, 2, 48, 25, 3, 2, 2, 2, 48, 30, 3, 2, 2, 2, 48, 34, 3, 2, 2, 2, 49, 64, 3, 2, 2, 2, 50, 51, 12, 9, 2, 2, 51, 52, 9, 3, 2, 2, 52, 63, 5, 4, 3, 10, 53, 54, 12, 8, 2, 2, 54, 55, 9, 4, 2, 2, 55, 63, 5, 4, 3, 9, 56, 57, 12, 7, 2, 2, 57, 58, 9, 5, 2, 2, 58, 63, 5, 4, 3, 8, 59, 60, 12, 6, 2, 2, 60, 61, 9, 6, 2, 2, 61, 63, 5, 4, 3, 7, 62, 50, 3, 2, 2, 2, 62, 53, 3, 2, 2, 2, 62, 56, 3, 2, 2, 2, 62, 59, 3, 2, 2, 2, 63, 66, 3, 2, 2, 2, 64, 62, 3, 2, 2, 2, 64, 65, 3, 2, 2, 2, 65, 5, 3, 2, 2, 2, 66, 64, 3, 2, 2, 2, 67, 68, 5, 10, 6, 2, 68, 7, 3, 2, 2, 2, 69, 70, 9, 7, 2, 2, 70, 9, 3, 2, 2, 2, 71, 72, 9, 8, 2, 2, 72, 11, 3, 2, 2, 2, 7, 41, 44, 48, 62, 64] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 84, 83, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 45, 10, 3, 12, 3, 14, 3, 48, 11, 3, 5, 3, 50, 10, 3, 3, 3, 3, 3, 5, 3, 54, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 70, 10, 3, 12, 3, 14, 3, 73, 11, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 2, 3, 4, 8, 2, 4, 6, 8, 10, 12, 2, 10, 3, 2, 6, 7, 4, 2, 15, 15, 74, 74, 4, 2, 62, 62, 68, 68, 4, 2, 42, 43, 53, 54, 4, 2, 38, 38, 40, 40, 3, 2, 3, 5, 3, 2, 26, 27, 3, 2, 28, 29, 2, 92, 2, 14, 3, 2, 2, 2, 4, 53, 3, 2, 2, 2, 6, 74, 3, 2, 2, 2, 8, 76, 3, 2, 2, 2, 10, 78, 3, 2, 2, 2, 12, 80, 3, 2, 2, 2, 14, 15, 5, 4, 3, 2, 15, 16, 7, 2, 2, 3, 16, 3, 3, 2, 2, 2, 17, 18, 8, 3, 1, 2, 18, 54, 7, 26, 2, 2, 19, 54, 7, 27, 2, 2, 20, 54, 7, 23, 2, 2, 21, 54, 7, 22, 2, 2, 22, 54, 9, 2, 2, 2, 23, 24, 5, 6, 4, 2, 24, 25, 5, 4, 3, 12, 25, 54, 3, 2, 2, 2, 26, 27, 7, 16, 2, 2, 27, 28, 5, 4, 3, 2, 28, 29, 7, 17, 2, 2, 29, 54, 3, 2, 2, 2, 30, 31, 7, 8, 2, 2, 31, 32, 7, 16, 2, 2, 32, 33, 5, 10, 6, 2, 33, 34, 7, 17, 2, 2, 34, 54, 3, 2, 2, 2, 35, 36, 7, 9, 2, 2, 36, 37, 7, 16, 2, 2, 37, 38, 7, 23, 2, 2, 38, 54, 7, 17, 2, 2, 39, 40, 5, 8, 5, 2, 40, 49, 7, 16, 2, 2, 41, 46, 5, 4, 3, 2, 42, 43, 7, 10, 2, 2, 43, 45, 5, 4, 3, 2, 44, 42, 3, 2, 2, 2, 45, 48, 3, 2, 2, 2, 46, 44, 3, 2, 2, 2, 46, 47, 3, 2, 2, 2, 47, 50, 3, 2, 2, 2, 48, 46, 3, 2, 2, 2, 49, 41, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 7, 17, 2, 2, 52, 54, 3, 2, 2, 2, 53, 17, 3, 2, 2, 2, 53, 19, 3, 2, 2, 2, 53, 20, 3, 2, 2, 2, 53, 21, 3, 2, 2, 2, 53, 22, 3, 2, 2, 2, 53, 23, 3, 2, 2, 2, 53, 26, 3, 2, 2, 2, 53, 30, 3, 2, 2, 2, 53, 35, 3, 2, 2, 2, 53, 39, 3, 2, 2, 2, 54, 71, 3, 2, 2, 2, 55, 56, 12, 9, 2, 2, 56, 57, 9, 3, 2, 2, 57, 70, 5, 4, 3, 10, 58, 59, 12, 8, 2, 2, 59, 60, 9, 4, 2, 2, 60, 70, 5, 4, 3, 9, 61, 62, 12, 7, 2, 2, 62, 63, 9, 5, 2, 2, 63, 70, 5, 4, 3, 8, 64, 65, 12, 6, 2, 2, 65, 66, 9, 6, 2, 2, 66, 70, 5, 4, 3, 7, 67, 68, 12, 11, 2, 2, 68, 70, 5, 6, 4, 2, 69, 55, 3, 2, 2, 2, 69, 58, 3, 2, 2, 2, 69, 61, 3, 2, 2, 2, 69, 64, 3, 2, 2, 2, 69, 67, 3, 2, 2, 2, 70, 73, 3, 2, 2, 2, 71, 69, 3, 2, 2, 2, 71, 72, 3, 2, 2, 2, 72, 5, 3, 2, 2, 2, 73, 71, 3, 2, 2, 2, 74, 75, 9, 7, 2, 2, 75, 7, 3, 2, 2, 2, 76, 77, 5, 12, 7, 2, 77, 9, 3, 2, 2, 2, 78, 79, 9, 8, 2, 2, 79, 11, 3, 2, 2, 2, 80, 81, 9, 9, 2, 2, 81, 13, 3, 2, 2, 2, 7, 46, 49, 53, 69, 71] \ No newline at end of file diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.js b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.js index b0cfbf389..416865157 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.js +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.js @@ -8,53 +8,58 @@ var grammarFileName = "BaserowFormula.g4"; var serializedATN = ["\u0003\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964", - "\u0003TJ\u0004\u0002\t\u0002\u0004\u0003\t\u0003\u0004\u0004\t\u0004", - "\u0004\u0005\t\u0005\u0004\u0006\t\u0006\u0003\u0002\u0003\u0002\u0003", - "\u0002\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", + "\u0003TS\u0004\u0002\t\u0002\u0004\u0003\t\u0003\u0004\u0004\t\u0004", + "\u0004\u0005\t\u0005\u0004\u0006\t\u0006\u0004\u0007\t\u0007\u0003\u0002", + "\u0003\u0002\u0003\u0002\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", - "\u0003\u0007\u0003(\n\u0003\f\u0003\u000e\u0003+\u000b\u0003\u0005\u0003", - "-\n\u0003\u0003\u0003\u0003\u0003\u0005\u00031\n\u0003\u0003\u0003\u0003", + "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0007\u0003", + "-\n\u0003\f\u0003\u000e\u00030\u000b\u0003\u0005\u00032\n\u0003\u0003", + "\u0003\u0003\u0003\u0005\u00036\n\u0003\u0003\u0003\u0003\u0003\u0003", "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", - "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0007\u0003?", - "\n\u0003\f\u0003\u000e\u0003B\u000b\u0003\u0003\u0004\u0003\u0004\u0003", - "\u0005\u0003\u0005\u0003\u0006\u0003\u0006\u0003\u0006\u0002\u0003\u0004", - "\u0007\u0002\u0004\u0006\b\n\u0002\t\u0003\u0002\u0006\u0007\u0004\u0002", - "\u000f\u000fJJ\u0004\u0002>>DD\u0004\u0002*+56\u0004\u0002&&((\u0003", - "\u0002\u001a\u001b\u0003\u0002\u001c\u001d\u0002R\u0002\f\u0003\u0002", - "\u0002\u0002\u00040\u0003\u0002\u0002\u0002\u0006C\u0003\u0002\u0002", - "\u0002\bE\u0003\u0002\u0002\u0002\nG\u0003\u0002\u0002\u0002\f\r\u0005", - "\u0004\u0003\u0002\r\u000e\u0007\u0002\u0002\u0003\u000e\u0003\u0003", - "\u0002\u0002\u0002\u000f\u0010\b\u0003\u0001\u0002\u00101\u0007\u001a", - "\u0002\u0002\u00111\u0007\u001b\u0002\u0002\u00121\u0007\u0017\u0002", - "\u0002\u00131\u0007\u0016\u0002\u0002\u00141\t\u0002\u0002\u0002\u0015", - "\u0016\u0007\u0010\u0002\u0002\u0016\u0017\u0005\u0004\u0003\u0002\u0017", - "\u0018\u0007\u0011\u0002\u0002\u00181\u0003\u0002\u0002\u0002\u0019", - "\u001a\u0007\b\u0002\u0002\u001a\u001b\u0007\u0010\u0002\u0002\u001b", - "\u001c\u0005\b\u0005\u0002\u001c\u001d\u0007\u0011\u0002\u0002\u001d", - "1\u0003\u0002\u0002\u0002\u001e\u001f\u0007\t\u0002\u0002\u001f \u0007", - "\u0010\u0002\u0002 !\u0007\u0017\u0002\u0002!1\u0007\u0011\u0002\u0002", - "\"#\u0005\u0006\u0004\u0002#,\u0007\u0010\u0002\u0002$)\u0005\u0004", - "\u0003\u0002%&\u0007\n\u0002\u0002&(\u0005\u0004\u0003\u0002\'%\u0003", - "\u0002\u0002\u0002(+\u0003\u0002\u0002\u0002)\'\u0003\u0002\u0002\u0002", - ")*\u0003\u0002\u0002\u0002*-\u0003\u0002\u0002\u0002+)\u0003\u0002\u0002", - "\u0002,$\u0003\u0002\u0002\u0002,-\u0003\u0002\u0002\u0002-.\u0003\u0002", - "\u0002\u0002./\u0007\u0011\u0002\u0002/1\u0003\u0002\u0002\u00020\u000f", - "\u0003\u0002\u0002\u00020\u0011\u0003\u0002\u0002\u00020\u0012\u0003", - "\u0002\u0002\u00020\u0013\u0003\u0002\u0002\u00020\u0014\u0003\u0002", - "\u0002\u00020\u0015\u0003\u0002\u0002\u00020\u0019\u0003\u0002\u0002", - "\u00020\u001e\u0003\u0002\u0002\u00020\"\u0003\u0002\u0002\u00021@\u0003", - "\u0002\u0002\u000223\f\t\u0002\u000234\t\u0003\u0002\u00024?\u0005\u0004", - "\u0003\n56\f\b\u0002\u000267\t\u0004\u0002\u00027?\u0005\u0004\u0003", - "\t89\f\u0007\u0002\u00029:\t\u0005\u0002\u0002:?\u0005\u0004\u0003\b", - ";<\f\u0006\u0002\u0002<=\t\u0006\u0002\u0002=?\u0005\u0004\u0003\u0007", - ">2\u0003\u0002\u0002\u0002>5\u0003\u0002\u0002\u0002>8\u0003\u0002\u0002", - "\u0002>;\u0003\u0002\u0002\u0002?B\u0003\u0002\u0002\u0002@>\u0003\u0002", - "\u0002\u0002@A\u0003\u0002\u0002\u0002A\u0005\u0003\u0002\u0002\u0002", - "B@\u0003\u0002\u0002\u0002CD\u0005\n\u0006\u0002D\u0007\u0003\u0002", - "\u0002\u0002EF\t\u0007\u0002\u0002F\t\u0003\u0002\u0002\u0002GH\t\b", - "\u0002\u0002H\u000b\u0003\u0002\u0002\u0002\u0007),0>@"].join(""); + "\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0007", + "\u0003F\n\u0003\f\u0003\u000e\u0003I\u000b\u0003\u0003\u0004\u0003\u0004", + "\u0003\u0005\u0003\u0005\u0003\u0006\u0003\u0006\u0003\u0007\u0003\u0007", + "\u0003\u0007\u0002\u0003\u0004\b\u0002\u0004\u0006\b\n\f\u0002\n\u0003", + "\u0002\u0006\u0007\u0004\u0002\u000f\u000fJJ\u0004\u0002>>DD\u0004\u0002", + "*+56\u0004\u0002&&((\u0003\u0002\u0003\u0005\u0003\u0002\u001a\u001b", + "\u0003\u0002\u001c\u001d\u0002\\\u0002\u000e\u0003\u0002\u0002\u0002", + "\u00045\u0003\u0002\u0002\u0002\u0006J\u0003\u0002\u0002\u0002\bL\u0003", + "\u0002\u0002\u0002\nN\u0003\u0002\u0002\u0002\fP\u0003\u0002\u0002\u0002", + "\u000e\u000f\u0005\u0004\u0003\u0002\u000f\u0010\u0007\u0002\u0002\u0003", + "\u0010\u0003\u0003\u0002\u0002\u0002\u0011\u0012\b\u0003\u0001\u0002", + "\u00126\u0007\u001a\u0002\u0002\u00136\u0007\u001b\u0002\u0002\u0014", + "6\u0007\u0017\u0002\u0002\u00156\u0007\u0016\u0002\u0002\u00166\t\u0002", + "\u0002\u0002\u0017\u0018\u0005\u0006\u0004\u0002\u0018\u0019\u0005\u0004", + "\u0003\f\u00196\u0003\u0002\u0002\u0002\u001a\u001b\u0007\u0010\u0002", + "\u0002\u001b\u001c\u0005\u0004\u0003\u0002\u001c\u001d\u0007\u0011\u0002", + "\u0002\u001d6\u0003\u0002\u0002\u0002\u001e\u001f\u0007\b\u0002\u0002", + "\u001f \u0007\u0010\u0002\u0002 !\u0005\n\u0006\u0002!\"\u0007\u0011", + "\u0002\u0002\"6\u0003\u0002\u0002\u0002#$\u0007\t\u0002\u0002$%\u0007", + "\u0010\u0002\u0002%&\u0007\u0017\u0002\u0002&6\u0007\u0011\u0002\u0002", + "\'(\u0005\b\u0005\u0002(1\u0007\u0010\u0002\u0002).\u0005\u0004\u0003", + "\u0002*+\u0007\n\u0002\u0002+-\u0005\u0004\u0003\u0002,*\u0003\u0002", + "\u0002\u0002-0\u0003\u0002\u0002\u0002.,\u0003\u0002\u0002\u0002./\u0003", + "\u0002\u0002\u0002/2\u0003\u0002\u0002\u00020.\u0003\u0002\u0002\u0002", + "1)\u0003\u0002\u0002\u000212\u0003\u0002\u0002\u000223\u0003\u0002\u0002", + "\u000234\u0007\u0011\u0002\u000246\u0003\u0002\u0002\u00025\u0011\u0003", + "\u0002\u0002\u00025\u0013\u0003\u0002\u0002\u00025\u0014\u0003\u0002", + "\u0002\u00025\u0015\u0003\u0002\u0002\u00025\u0016\u0003\u0002\u0002", + "\u00025\u0017\u0003\u0002\u0002\u00025\u001a\u0003\u0002\u0002\u0002", + "5\u001e\u0003\u0002\u0002\u00025#\u0003\u0002\u0002\u00025\'\u0003\u0002", + "\u0002\u00026G\u0003\u0002\u0002\u000278\f\t\u0002\u000289\t\u0003\u0002", + "\u00029F\u0005\u0004\u0003\n:;\f\b\u0002\u0002;<\t\u0004\u0002\u0002", + "<F\u0005\u0004\u0003\t=>\f\u0007\u0002\u0002>?\t\u0005\u0002\u0002?", + "F\u0005\u0004\u0003\b@A\f\u0006\u0002\u0002AB\t\u0006\u0002\u0002BF", + "\u0005\u0004\u0003\u0007CD\f\u000b\u0002\u0002DF\u0005\u0006\u0004\u0002", + "E7\u0003\u0002\u0002\u0002E:\u0003\u0002\u0002\u0002E=\u0003\u0002\u0002", + "\u0002E@\u0003\u0002\u0002\u0002EC\u0003\u0002\u0002\u0002FI\u0003\u0002", + "\u0002\u0002GE\u0003\u0002\u0002\u0002GH\u0003\u0002\u0002\u0002H\u0005", + "\u0003\u0002\u0002\u0002IG\u0003\u0002\u0002\u0002JK\t\u0007\u0002\u0002", + "K\u0007\u0003\u0002\u0002\u0002LM\u0005\f\u0007\u0002M\t\u0003\u0002", + "\u0002\u0002NO\t\b\u0002\u0002O\u000b\u0003\u0002\u0002\u0002PQ\t\t", + "\u0002\u0002Q\r\u0003\u0002\u0002\u0002\u0007.15EG"].join(""); var atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN); @@ -76,7 +81,7 @@ var literalNames = [ null, null, null, null, null, null, null, null, "','", "'~='", "'~>=~'", "'~>~'", "'~<=~'", "'~<~'", "'~*'", "'~~'", "';'" ]; -var symbolicNames = [ null, "WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", +var symbolicNames = [ null, "BLOCK_COMMENT", "LINE_COMMENT", "WHITESPACE", "TRUE", "FALSE", "FIELD", "FIELDBYID", "COMMA", "COLON", "COLON_COLON", "DOLLAR", "DOLLAR_DOLLAR", "STAR", "OPEN_PAREN", "CLOSE_PAREN", "OPEN_BRACKET", "CLOSE_BRACKET", @@ -96,7 +101,8 @@ var symbolicNames = [ null, "WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", "TIL_GT_TIL", "TIL_LTE_TIL", "TIL_LT_TIL", "TIL_STAR", "TIL_TIL", "SEMI", "ErrorCharacter" ]; -var ruleNames = [ "root", "expr", "func_name", "field_reference", "identifier" ]; +var ruleNames = [ "root", "expr", "ws_or_comment", "func_name", "field_reference", + "identifier" ]; function BaserowFormula (input) { antlr4.Parser.call(this, input); @@ -117,9 +123,9 @@ Object.defineProperty(BaserowFormula.prototype, "atn", { }); BaserowFormula.EOF = antlr4.Token.EOF; -BaserowFormula.WHITESPACE = 1; -BaserowFormula.BLOCK_COMMENT = 2; -BaserowFormula.LINE_COMMENT = 3; +BaserowFormula.BLOCK_COMMENT = 1; +BaserowFormula.LINE_COMMENT = 2; +BaserowFormula.WHITESPACE = 3; BaserowFormula.TRUE = 4; BaserowFormula.FALSE = 5; BaserowFormula.FIELD = 6; @@ -202,9 +208,10 @@ BaserowFormula.ErrorCharacter = 82; BaserowFormula.RULE_root = 0; BaserowFormula.RULE_expr = 1; -BaserowFormula.RULE_func_name = 2; -BaserowFormula.RULE_field_reference = 3; -BaserowFormula.RULE_identifier = 4; +BaserowFormula.RULE_ws_or_comment = 2; +BaserowFormula.RULE_func_name = 3; +BaserowFormula.RULE_field_reference = 4; +BaserowFormula.RULE_identifier = 5; function RootContext(parser, parent, invokingState) { @@ -262,9 +269,9 @@ BaserowFormula.prototype.root = function() { this.enterRule(localctx, 0, BaserowFormula.RULE_root); try { this.enterOuterAlt(localctx, 1); - this.state = 10; + this.state = 12; this.expr(0); - this.state = 11; + this.state = 13; this.match(BaserowFormula.EOF); } catch (re) { if(re instanceof antlr4.error.RecognitionException) { @@ -471,6 +478,45 @@ BooleanLiteralContext.prototype.accept = function(visitor) { }; +function RightWhitespaceOrCommentsContext(parser, ctx) { + ExprContext.call(this, parser); + ExprContext.prototype.copyFrom.call(this, ctx); + return this; +} + +RightWhitespaceOrCommentsContext.prototype = Object.create(ExprContext.prototype); +RightWhitespaceOrCommentsContext.prototype.constructor = RightWhitespaceOrCommentsContext; + +BaserowFormula.RightWhitespaceOrCommentsContext = RightWhitespaceOrCommentsContext; + +RightWhitespaceOrCommentsContext.prototype.expr = function() { + return this.getTypedRuleContext(ExprContext,0); +}; + +RightWhitespaceOrCommentsContext.prototype.ws_or_comment = function() { + return this.getTypedRuleContext(Ws_or_commentContext,0); +}; +RightWhitespaceOrCommentsContext.prototype.enterRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.enterRightWhitespaceOrComments(this); + } +}; + +RightWhitespaceOrCommentsContext.prototype.exitRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.exitRightWhitespaceOrComments(this); + } +}; + +RightWhitespaceOrCommentsContext.prototype.accept = function(visitor) { + if ( visitor instanceof BaserowFormulaVisitor ) { + return visitor.visitRightWhitespaceOrComments(this); + } else { + return visitor.visitChildren(this); + } +}; + + function DecimalLiteralContext(parser, ctx) { ExprContext.call(this, parser); ExprContext.prototype.copyFrom.call(this, ctx); @@ -506,6 +552,45 @@ DecimalLiteralContext.prototype.accept = function(visitor) { }; +function LeftWhitespaceOrCommentsContext(parser, ctx) { + ExprContext.call(this, parser); + ExprContext.prototype.copyFrom.call(this, ctx); + return this; +} + +LeftWhitespaceOrCommentsContext.prototype = Object.create(ExprContext.prototype); +LeftWhitespaceOrCommentsContext.prototype.constructor = LeftWhitespaceOrCommentsContext; + +BaserowFormula.LeftWhitespaceOrCommentsContext = LeftWhitespaceOrCommentsContext; + +LeftWhitespaceOrCommentsContext.prototype.ws_or_comment = function() { + return this.getTypedRuleContext(Ws_or_commentContext,0); +}; + +LeftWhitespaceOrCommentsContext.prototype.expr = function() { + return this.getTypedRuleContext(ExprContext,0); +}; +LeftWhitespaceOrCommentsContext.prototype.enterRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.enterLeftWhitespaceOrComments(this); + } +}; + +LeftWhitespaceOrCommentsContext.prototype.exitRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.exitLeftWhitespaceOrComments(this); + } +}; + +LeftWhitespaceOrCommentsContext.prototype.accept = function(visitor) { + if ( visitor instanceof BaserowFormulaVisitor ) { + return visitor.visitLeftWhitespaceOrComments(this); + } else { + return visitor.visitChildren(this); + } +}; + + function FunctionCallContext(parser, ctx) { ExprContext.call(this, parser); ExprContext.prototype.copyFrom.call(this, ctx); @@ -751,7 +836,7 @@ BaserowFormula.prototype.expr = function(_p) { var _la = 0; // Token type try { this.enterOuterAlt(localctx, 1); - this.state = 46; + this.state = 51; this._errHandler.sync(this); switch(this._input.LA(1)) { case BaserowFormula.SINGLEQ_STRING_LITERAL: @@ -759,28 +844,28 @@ BaserowFormula.prototype.expr = function(_p) { this._ctx = localctx; _prevctx = localctx; - this.state = 14; + this.state = 16; this.match(BaserowFormula.SINGLEQ_STRING_LITERAL); break; case BaserowFormula.DOUBLEQ_STRING_LITERAL: localctx = new StringLiteralContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 15; + this.state = 17; this.match(BaserowFormula.DOUBLEQ_STRING_LITERAL); break; case BaserowFormula.INTEGER_LITERAL: localctx = new IntegerLiteralContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 16; + this.state = 18; this.match(BaserowFormula.INTEGER_LITERAL); break; case BaserowFormula.NUMERIC_LITERAL: localctx = new DecimalLiteralContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 17; + this.state = 19; this.match(BaserowFormula.NUMERIC_LITERAL); break; case BaserowFormula.TRUE: @@ -788,7 +873,7 @@ BaserowFormula.prototype.expr = function(_p) { localctx = new BooleanLiteralContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 18; + this.state = 20; _la = this._input.LA(1); if(!(_la===BaserowFormula.TRUE || _la===BaserowFormula.FALSE)) { this._errHandler.recoverInline(this); @@ -798,41 +883,52 @@ BaserowFormula.prototype.expr = function(_p) { this.consume(); } break; + case BaserowFormula.BLOCK_COMMENT: + case BaserowFormula.LINE_COMMENT: + case BaserowFormula.WHITESPACE: + localctx = new LeftWhitespaceOrCommentsContext(this, localctx); + this._ctx = localctx; + _prevctx = localctx; + this.state = 21; + this.ws_or_comment(); + this.state = 22; + this.expr(10); + break; case BaserowFormula.OPEN_PAREN: localctx = new BracketsContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 19; + this.state = 24; this.match(BaserowFormula.OPEN_PAREN); - this.state = 20; + this.state = 25; this.expr(0); - this.state = 21; + this.state = 26; this.match(BaserowFormula.CLOSE_PAREN); break; case BaserowFormula.FIELD: localctx = new FieldReferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 23; + this.state = 28; this.match(BaserowFormula.FIELD); - this.state = 24; + this.state = 29; this.match(BaserowFormula.OPEN_PAREN); - this.state = 25; + this.state = 30; this.field_reference(); - this.state = 26; + this.state = 31; this.match(BaserowFormula.CLOSE_PAREN); break; case BaserowFormula.FIELDBYID: localctx = new FieldByIdReferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 28; + this.state = 33; this.match(BaserowFormula.FIELDBYID); - this.state = 29; + this.state = 34; this.match(BaserowFormula.OPEN_PAREN); - this.state = 30; + this.state = 35; this.match(BaserowFormula.INTEGER_LITERAL); - this.state = 31; + this.state = 36; this.match(BaserowFormula.CLOSE_PAREN); break; case BaserowFormula.IDENTIFIER: @@ -840,38 +936,38 @@ BaserowFormula.prototype.expr = function(_p) { localctx = new FunctionCallContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 32; + this.state = 37; this.func_name(); - this.state = 33; + this.state = 38; this.match(BaserowFormula.OPEN_PAREN); - this.state = 42; + this.state = 47; this._errHandler.sync(this); _la = this._input.LA(1); - if((((_la) & ~0x1f) == 0 && ((1 << _la) & ((1 << BaserowFormula.TRUE) | (1 << BaserowFormula.FALSE) | (1 << BaserowFormula.FIELD) | (1 << BaserowFormula.FIELDBYID) | (1 << BaserowFormula.OPEN_PAREN) | (1 << BaserowFormula.NUMERIC_LITERAL) | (1 << BaserowFormula.INTEGER_LITERAL) | (1 << BaserowFormula.SINGLEQ_STRING_LITERAL) | (1 << BaserowFormula.DOUBLEQ_STRING_LITERAL) | (1 << BaserowFormula.IDENTIFIER) | (1 << BaserowFormula.IDENTIFIER_UNICODE))) !== 0)) { - this.state = 34; - this.expr(0); + if((((_la) & ~0x1f) == 0 && ((1 << _la) & ((1 << BaserowFormula.BLOCK_COMMENT) | (1 << BaserowFormula.LINE_COMMENT) | (1 << BaserowFormula.WHITESPACE) | (1 << BaserowFormula.TRUE) | (1 << BaserowFormula.FALSE) | (1 << BaserowFormula.FIELD) | (1 << BaserowFormula.FIELDBYID) | (1 << BaserowFormula.OPEN_PAREN) | (1 << BaserowFormula.NUMERIC_LITERAL) | (1 << BaserowFormula.INTEGER_LITERAL) | (1 << BaserowFormula.SINGLEQ_STRING_LITERAL) | (1 << BaserowFormula.DOUBLEQ_STRING_LITERAL) | (1 << BaserowFormula.IDENTIFIER) | (1 << BaserowFormula.IDENTIFIER_UNICODE))) !== 0)) { this.state = 39; + this.expr(0); + this.state = 44; this._errHandler.sync(this); _la = this._input.LA(1); while(_la===BaserowFormula.COMMA) { - this.state = 35; + this.state = 40; this.match(BaserowFormula.COMMA); - this.state = 36; - this.expr(0); this.state = 41; + this.expr(0); + this.state = 46; this._errHandler.sync(this); _la = this._input.LA(1); } } - this.state = 44; + this.state = 49; this.match(BaserowFormula.CLOSE_PAREN); break; default: throw new antlr4.error.NoViableAltException(this); } this._ctx.stop = this._input.LT(-1); - this.state = 62; + this.state = 69; this._errHandler.sync(this); var _alt = this._interp.adaptivePredict(this._input,4,this._ctx) while(_alt!=2 && _alt!=antlr4.atn.ATN.INVALID_ALT_NUMBER) { @@ -880,18 +976,18 @@ BaserowFormula.prototype.expr = function(_p) { this.triggerExitRuleEvent(); } _prevctx = localctx; - this.state = 60; + this.state = 67; this._errHandler.sync(this); var la_ = this._interp.adaptivePredict(this._input,3,this._ctx); switch(la_) { case 1: localctx = new BinaryOpContext(this, new ExprContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, BaserowFormula.RULE_expr); - this.state = 48; + this.state = 53; if (!( this.precpred(this._ctx, 7))) { throw new antlr4.error.FailedPredicateException(this, "this.precpred(this._ctx, 7)"); } - this.state = 49; + this.state = 54; localctx.op = this._input.LT(1); _la = this._input.LA(1); if(!(_la===BaserowFormula.STAR || _la===BaserowFormula.SLASH)) { @@ -901,18 +997,18 @@ BaserowFormula.prototype.expr = function(_p) { this._errHandler.reportMatch(this); this.consume(); } - this.state = 50; + this.state = 55; this.expr(8); break; case 2: localctx = new BinaryOpContext(this, new ExprContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, BaserowFormula.RULE_expr); - this.state = 51; + this.state = 56; if (!( this.precpred(this._ctx, 6))) { throw new antlr4.error.FailedPredicateException(this, "this.precpred(this._ctx, 6)"); } - this.state = 52; + this.state = 57; localctx.op = this._input.LT(1); _la = this._input.LA(1); if(!(_la===BaserowFormula.MINUS || _la===BaserowFormula.PLUS)) { @@ -922,18 +1018,18 @@ BaserowFormula.prototype.expr = function(_p) { this._errHandler.reportMatch(this); this.consume(); } - this.state = 53; + this.state = 58; this.expr(7); break; case 3: localctx = new BinaryOpContext(this, new ExprContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, BaserowFormula.RULE_expr); - this.state = 54; + this.state = 59; if (!( this.precpred(this._ctx, 5))) { throw new antlr4.error.FailedPredicateException(this, "this.precpred(this._ctx, 5)"); } - this.state = 55; + this.state = 60; localctx.op = this._input.LT(1); _la = this._input.LA(1); if(!(((((_la - 40)) & ~0x1f) == 0 && ((1 << (_la - 40)) & ((1 << (BaserowFormula.GT - 40)) | (1 << (BaserowFormula.GTE - 40)) | (1 << (BaserowFormula.LT - 40)) | (1 << (BaserowFormula.LTE - 40)))) !== 0))) { @@ -943,18 +1039,18 @@ BaserowFormula.prototype.expr = function(_p) { this._errHandler.reportMatch(this); this.consume(); } - this.state = 56; + this.state = 61; this.expr(6); break; case 4: localctx = new BinaryOpContext(this, new ExprContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, BaserowFormula.RULE_expr); - this.state = 57; + this.state = 62; if (!( this.precpred(this._ctx, 4))) { throw new antlr4.error.FailedPredicateException(this, "this.precpred(this._ctx, 4)"); } - this.state = 58; + this.state = 63; localctx.op = this._input.LT(1); _la = this._input.LA(1); if(!(_la===BaserowFormula.BANG_EQUAL || _la===BaserowFormula.EQUAL)) { @@ -964,13 +1060,24 @@ BaserowFormula.prototype.expr = function(_p) { this._errHandler.reportMatch(this); this.consume(); } - this.state = 59; + this.state = 64; this.expr(5); break; + case 5: + localctx = new RightWhitespaceOrCommentsContext(this, new ExprContext(this, _parentctx, _parentState)); + this.pushNewRecursionContext(localctx, _startState, BaserowFormula.RULE_expr); + this.state = 65; + if (!( this.precpred(this._ctx, 9))) { + throw new antlr4.error.FailedPredicateException(this, "this.precpred(this._ctx, 9)"); + } + this.state = 66; + this.ws_or_comment(); + break; + } } - this.state = 64; + this.state = 71; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input,4,this._ctx); } @@ -990,6 +1097,90 @@ BaserowFormula.prototype.expr = function(_p) { }; +function Ws_or_commentContext(parser, parent, invokingState) { + if(parent===undefined) { + parent = null; + } + if(invokingState===undefined || invokingState===null) { + invokingState = -1; + } + antlr4.ParserRuleContext.call(this, parent, invokingState); + this.parser = parser; + this.ruleIndex = BaserowFormula.RULE_ws_or_comment; + return this; +} + +Ws_or_commentContext.prototype = Object.create(antlr4.ParserRuleContext.prototype); +Ws_or_commentContext.prototype.constructor = Ws_or_commentContext; + +Ws_or_commentContext.prototype.BLOCK_COMMENT = function() { + return this.getToken(BaserowFormula.BLOCK_COMMENT, 0); +}; + +Ws_or_commentContext.prototype.LINE_COMMENT = function() { + return this.getToken(BaserowFormula.LINE_COMMENT, 0); +}; + +Ws_or_commentContext.prototype.WHITESPACE = function() { + return this.getToken(BaserowFormula.WHITESPACE, 0); +}; + +Ws_or_commentContext.prototype.enterRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.enterWs_or_comment(this); + } +}; + +Ws_or_commentContext.prototype.exitRule = function(listener) { + if(listener instanceof BaserowFormulaListener ) { + listener.exitWs_or_comment(this); + } +}; + +Ws_or_commentContext.prototype.accept = function(visitor) { + if ( visitor instanceof BaserowFormulaVisitor ) { + return visitor.visitWs_or_comment(this); + } else { + return visitor.visitChildren(this); + } +}; + + + + +BaserowFormula.Ws_or_commentContext = Ws_or_commentContext; + +BaserowFormula.prototype.ws_or_comment = function() { + + var localctx = new Ws_or_commentContext(this, this._ctx, this.state); + this.enterRule(localctx, 4, BaserowFormula.RULE_ws_or_comment); + var _la = 0; // Token type + try { + this.enterOuterAlt(localctx, 1); + this.state = 72; + _la = this._input.LA(1); + if(!((((_la) & ~0x1f) == 0 && ((1 << _la) & ((1 << BaserowFormula.BLOCK_COMMENT) | (1 << BaserowFormula.LINE_COMMENT) | (1 << BaserowFormula.WHITESPACE))) !== 0))) { + this._errHandler.recoverInline(this); + } + else { + this._errHandler.reportMatch(this); + this.consume(); + } + } catch (re) { + if(re instanceof antlr4.error.RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localctx; +}; + + function Func_nameContext(parser, parent, invokingState) { if(parent===undefined) { parent = null; @@ -1038,10 +1229,10 @@ BaserowFormula.Func_nameContext = Func_nameContext; BaserowFormula.prototype.func_name = function() { var localctx = new Func_nameContext(this, this._ctx, this.state); - this.enterRule(localctx, 4, BaserowFormula.RULE_func_name); + this.enterRule(localctx, 6, BaserowFormula.RULE_func_name); try { this.enterOuterAlt(localctx, 1); - this.state = 65; + this.state = 74; this.identifier(); } catch (re) { if(re instanceof antlr4.error.RecognitionException) { @@ -1110,11 +1301,11 @@ BaserowFormula.Field_referenceContext = Field_referenceContext; BaserowFormula.prototype.field_reference = function() { var localctx = new Field_referenceContext(this, this._ctx, this.state); - this.enterRule(localctx, 6, BaserowFormula.RULE_field_reference); + this.enterRule(localctx, 8, BaserowFormula.RULE_field_reference); var _la = 0; // Token type try { this.enterOuterAlt(localctx, 1); - this.state = 67; + this.state = 76; _la = this._input.LA(1); if(!(_la===BaserowFormula.SINGLEQ_STRING_LITERAL || _la===BaserowFormula.DOUBLEQ_STRING_LITERAL)) { this._errHandler.recoverInline(this); @@ -1190,11 +1381,11 @@ BaserowFormula.IdentifierContext = IdentifierContext; BaserowFormula.prototype.identifier = function() { var localctx = new IdentifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 8, BaserowFormula.RULE_identifier); + this.enterRule(localctx, 10, BaserowFormula.RULE_identifier); var _la = 0; // Token type try { this.enterOuterAlt(localctx, 1); - this.state = 69; + this.state = 78; _la = this._input.LA(1); if(!(_la===BaserowFormula.IDENTIFIER || _la===BaserowFormula.IDENTIFIER_UNICODE)) { this._errHandler.recoverInline(this); @@ -1237,6 +1428,8 @@ BaserowFormula.prototype.expr_sempred = function(localctx, predIndex) { return this.precpred(this._ctx, 5); case 3: return this.precpred(this._ctx, 4); + case 4: + return this.precpred(this._ctx, 9); default: throw "No predicate with index:" + predIndex; } diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.tokens b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.tokens index 805982389..1b6673037 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormula.tokens +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormula.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.interp b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.interp index 6844f1e6a..b2b0f34b7 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.interp +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.interp @@ -85,9 +85,9 @@ null token symbolic names: null -WHITESPACE BLOCK_COMMENT LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -169,9 +169,6 @@ SEMI ErrorCharacter rule names: -WHITESPACE -BLOCK_COMMENT -LINE_COMMENT A B C @@ -204,6 +201,9 @@ DEC_DIGIT DQUOTA_STRING SQUOTA_STRING BQUOTA_STRING +BLOCK_COMMENT +LINE_COMMENT +WHITESPACE TRUE FALSE FIELD @@ -292,4 +292,4 @@ mode names: DEFAULT_MODE atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 84, 650, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 3, 2, 6, 2, 233, 10, 2, 13, 2, 14, 2, 234, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 243, 10, 3, 12, 3, 14, 3, 246, 11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 257, 10, 4, 12, 4, 14, 4, 260, 11, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 7, 34, 328, 10, 34, 12, 34, 14, 34, 331, 11, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 7, 35, 339, 10, 35, 12, 35, 14, 35, 342, 11, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 7, 36, 352, 10, 36, 12, 36, 14, 36, 355, 11, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 7, 51, 413, 10, 51, 12, 51, 14, 51, 416, 11, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 5, 53, 424, 10, 53, 3, 53, 6, 53, 427, 10, 53, 13, 53, 14, 53, 428, 3, 53, 3, 53, 6, 53, 433, 10, 53, 13, 53, 14, 53, 434, 3, 53, 3, 53, 7, 53, 439, 10, 53, 12, 53, 14, 53, 442, 11, 53, 3, 53, 6, 53, 445, 10, 53, 13, 53, 14, 53, 446, 5, 53, 449, 10, 53, 3, 54, 5, 54, 452, 10, 54, 3, 54, 6, 54, 455, 10, 54, 13, 54, 14, 54, 456, 3, 54, 3, 54, 6, 54, 461, 10, 54, 13, 54, 14, 54, 462, 5, 54, 465, 10, 54, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 7, 59, 478, 10, 59, 12, 59, 14, 59, 481, 11, 59, 3, 60, 3, 60, 7, 60, 485, 10, 60, 12, 60, 14, 60, 488, 11, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 82, 3, 83, 3, 83, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 96, 3, 97, 3, 97, 3, 97, 3, 97, 3, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 101, 3, 102, 3, 102, 3, 102, 3, 103, 3, 103, 3, 103, 3, 104, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 107, 3, 108, 3, 108, 3, 108, 3, 108, 3, 108, 3, 109, 3, 109, 3, 109, 3, 109, 3, 110, 3, 110, 3, 110, 3, 110, 3, 110, 3, 111, 3, 111, 3, 111, 3, 111, 3, 112, 3, 112, 3, 112, 3, 113, 3, 113, 3, 113, 3, 114, 3, 114, 3, 115, 3, 115, 4, 244, 258, 2, 116, 3, 3, 5, 4, 7, 5, 9, 2, 11, 2, 13, 2, 15, 2, 17, 2, 19, 2, 21, 2, 23, 2, 25, 2, 27, 2, 29, 2, 31, 2, 33, 2, 35, 2, 37, 2, 39, 2, 41, 2, 43, 2, 45, 2, 47, 2, 49, 2, 51, 2, 53, 2, 55, 2, 57, 2, 59, 2, 61, 2, 63, 2, 65, 2, 67, 2, 69, 2, 71, 2, 73, 6, 75, 7, 77, 8, 79, 9, 81, 10, 83, 11, 85, 12, 87, 13, 89, 14, 91, 15, 93, 16, 95, 17, 97, 18, 99, 19, 101, 20, 103, 21, 105, 22, 107, 23, 109, 24, 111, 25, 113, 26, 115, 27, 117, 28, 119, 29, 121, 30, 123, 31, 125, 32, 127, 33, 129, 34, 131, 35, 133, 36, 135, 37, 137, 38, 139, 39, 141, 40, 143, 41, 145, 42, 147, 43, 149, 44, 151, 45, 153, 46, 155, 47, 157, 48, 159, 49, 161, 50, 163, 51, 165, 52, 167, 53, 169, 54, 171, 55, 173, 56, 175, 57, 177, 58, 179, 59, 181, 60, 183, 61, 185, 62, 187, 63, 189, 64, 191, 65, 193, 66, 195, 67, 197, 68, 199, 69, 201, 70, 203, 71, 205, 72, 207, 73, 209, 74, 211, 75, 213, 76, 215, 77, 217, 78, 219, 79, 221, 80, 223, 81, 225, 82, 227, 83, 229, 84, 3, 2, 38, 5, 2, 11, 12, 15, 15, 34, 34, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 4, 2, 50, 59, 67, 72, 3, 2, 50, 59, 4, 2, 36, 36, 94, 94, 4, 2, 41, 41, 94, 94, 4, 2, 94, 94, 98, 98, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 6, 2, 67, 92, 97, 97, 99, 124, 163, 1, 7, 2, 50, 59, 67, 92, 97, 97, 99, 124, 163, 1, 2, 640, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 157, 3, 2, 2, 2, 2, 159, 3, 2, 2, 2, 2, 161, 3, 2, 2, 2, 2, 163, 3, 2, 2, 2, 2, 165, 3, 2, 2, 2, 2, 167, 3, 2, 2, 2, 2, 169, 3, 2, 2, 2, 2, 171, 3, 2, 2, 2, 2, 173, 3, 2, 2, 2, 2, 175, 3, 2, 2, 2, 2, 177, 3, 2, 2, 2, 2, 179, 3, 2, 2, 2, 2, 181, 3, 2, 2, 2, 2, 183, 3, 2, 2, 2, 2, 185, 3, 2, 2, 2, 2, 187, 3, 2, 2, 2, 2, 189, 3, 2, 2, 2, 2, 191, 3, 2, 2, 2, 2, 193, 3, 2, 2, 2, 2, 195, 3, 2, 2, 2, 2, 197, 3, 2, 2, 2, 2, 199, 3, 2, 2, 2, 2, 201, 3, 2, 2, 2, 2, 203, 3, 2, 2, 2, 2, 205, 3, 2, 2, 2, 2, 207, 3, 2, 2, 2, 2, 209, 3, 2, 2, 2, 2, 211, 3, 2, 2, 2, 2, 213, 3, 2, 2, 2, 2, 215, 3, 2, 2, 2, 2, 217, 3, 2, 2, 2, 2, 219, 3, 2, 2, 2, 2, 221, 3, 2, 2, 2, 2, 223, 3, 2, 2, 2, 2, 225, 3, 2, 2, 2, 2, 227, 3, 2, 2, 2, 2, 229, 3, 2, 2, 2, 3, 232, 3, 2, 2, 2, 5, 238, 3, 2, 2, 2, 7, 252, 3, 2, 2, 2, 9, 265, 3, 2, 2, 2, 11, 267, 3, 2, 2, 2, 13, 269, 3, 2, 2, 2, 15, 271, 3, 2, 2, 2, 17, 273, 3, 2, 2, 2, 19, 275, 3, 2, 2, 2, 21, 277, 3, 2, 2, 2, 23, 279, 3, 2, 2, 2, 25, 281, 3, 2, 2, 2, 27, 283, 3, 2, 2, 2, 29, 285, 3, 2, 2, 2, 31, 287, 3, 2, 2, 2, 33, 289, 3, 2, 2, 2, 35, 291, 3, 2, 2, 2, 37, 293, 3, 2, 2, 2, 39, 295, 3, 2, 2, 2, 41, 297, 3, 2, 2, 2, 43, 299, 3, 2, 2, 2, 45, 301, 3, 2, 2, 2, 47, 303, 3, 2, 2, 2, 49, 305, 3, 2, 2, 2, 51, 307, 3, 2, 2, 2, 53, 309, 3, 2, 2, 2, 55, 311, 3, 2, 2, 2, 57, 313, 3, 2, 2, 2, 59, 315, 3, 2, 2, 2, 61, 317, 3, 2, 2, 2, 63, 319, 3, 2, 2, 2, 65, 321, 3, 2, 2, 2, 67, 323, 3, 2, 2, 2, 69, 334, 3, 2, 2, 2, 71, 345, 3, 2, 2, 2, 73, 358, 3, 2, 2, 2, 75, 363, 3, 2, 2, 2, 77, 369, 3, 2, 2, 2, 79, 375, 3, 2, 2, 2, 81, 387, 3, 2, 2, 2, 83, 389, 3, 2, 2, 2, 85, 391, 3, 2, 2, 2, 87, 394, 3, 2, 2, 2, 89, 396, 3, 2, 2, 2, 91, 399, 3, 2, 2, 2, 93, 401, 3, 2, 2, 2, 95, 403, 3, 2, 2, 2, 97, 405, 3, 2, 2, 2, 99, 407, 3, 2, 2, 2, 101, 409, 3, 2, 2, 2, 103, 419, 3, 2, 2, 2, 105, 423, 3, 2, 2, 2, 107, 451, 3, 2, 2, 2, 109, 466, 3, 2, 2, 2, 111, 469, 3, 2, 2, 2, 113, 471, 3, 2, 2, 2, 115, 473, 3, 2, 2, 2, 117, 475, 3, 2, 2, 2, 119, 482, 3, 2, 2, 2, 121, 489, 3, 2, 2, 2, 123, 491, 3, 2, 2, 2, 125, 494, 3, 2, 2, 2, 127, 497, 3, 2, 2, 2, 129, 500, 3, 2, 2, 2, 131, 503, 3, 2, 2, 2, 133, 505, 3, 2, 2, 2, 135, 507, 3, 2, 2, 2, 137, 510, 3, 2, 2, 2, 139, 513, 3, 2, 2, 2, 141, 515, 3, 2, 2, 2, 143, 517, 3, 2, 2, 2, 145, 520, 3, 2, 2, 2, 147, 522, 3, 2, 2, 2, 149, 525, 3, 2, 2, 2, 151, 528, 3, 2, 2, 2, 153, 530, 3, 2, 2, 2, 155, 533, 3, 2, 2, 2, 157, 536, 3, 2, 2, 2, 159, 540, 3, 2, 2, 2, 161, 543, 3, 2, 2, 2, 163, 546, 3, 2, 2, 2, 165, 550, 3, 2, 2, 2, 167, 554, 3, 2, 2, 2, 169, 556, 3, 2, 2, 2, 171, 559, 3, 2, 2, 2, 173, 562, 3, 2, 2, 2, 175, 565, 3, 2, 2, 2, 177, 568, 3, 2, 2, 2, 179, 572, 3, 2, 2, 2, 181, 575, 3, 2, 2, 2, 183, 579, 3, 2, 2, 2, 185, 583, 3, 2, 2, 2, 187, 585, 3, 2, 2, 2, 189, 587, 3, 2, 2, 2, 191, 589, 3, 2, 2, 2, 193, 592, 3, 2, 2, 2, 195, 596, 3, 2, 2, 2, 197, 599, 3, 2, 2, 2, 199, 601, 3, 2, 2, 2, 201, 603, 3, 2, 2, 2, 203, 606, 3, 2, 2, 2, 205, 609, 3, 2, 2, 2, 207, 612, 3, 2, 2, 2, 209, 615, 3, 2, 2, 2, 211, 617, 3, 2, 2, 2, 213, 619, 3, 2, 2, 2, 215, 622, 3, 2, 2, 2, 217, 627, 3, 2, 2, 2, 219, 631, 3, 2, 2, 2, 221, 636, 3, 2, 2, 2, 223, 640, 3, 2, 2, 2, 225, 643, 3, 2, 2, 2, 227, 646, 3, 2, 2, 2, 229, 648, 3, 2, 2, 2, 231, 233, 9, 2, 2, 2, 232, 231, 3, 2, 2, 2, 233, 234, 3, 2, 2, 2, 234, 232, 3, 2, 2, 2, 234, 235, 3, 2, 2, 2, 235, 236, 3, 2, 2, 2, 236, 237, 8, 2, 2, 2, 237, 4, 3, 2, 2, 2, 238, 239, 7, 49, 2, 2, 239, 240, 7, 44, 2, 2, 240, 244, 3, 2, 2, 2, 241, 243, 11, 2, 2, 2, 242, 241, 3, 2, 2, 2, 243, 246, 3, 2, 2, 2, 244, 245, 3, 2, 2, 2, 244, 242, 3, 2, 2, 2, 245, 247, 3, 2, 2, 2, 246, 244, 3, 2, 2, 2, 247, 248, 7, 44, 2, 2, 248, 249, 7, 49, 2, 2, 249, 250, 3, 2, 2, 2, 250, 251, 8, 3, 2, 2, 251, 6, 3, 2, 2, 2, 252, 253, 7, 49, 2, 2, 253, 254, 7, 49, 2, 2, 254, 258, 3, 2, 2, 2, 255, 257, 11, 2, 2, 2, 256, 255, 3, 2, 2, 2, 257, 260, 3, 2, 2, 2, 258, 259, 3, 2, 2, 2, 258, 256, 3, 2, 2, 2, 259, 261, 3, 2, 2, 2, 260, 258, 3, 2, 2, 2, 261, 262, 7, 12, 2, 2, 262, 263, 3, 2, 2, 2, 263, 264, 8, 4, 2, 2, 264, 8, 3, 2, 2, 2, 265, 266, 9, 3, 2, 2, 266, 10, 3, 2, 2, 2, 267, 268, 9, 4, 2, 2, 268, 12, 3, 2, 2, 2, 269, 270, 9, 5, 2, 2, 270, 14, 3, 2, 2, 2, 271, 272, 9, 6, 2, 2, 272, 16, 3, 2, 2, 2, 273, 274, 9, 7, 2, 2, 274, 18, 3, 2, 2, 2, 275, 276, 9, 8, 2, 2, 276, 20, 3, 2, 2, 2, 277, 278, 9, 9, 2, 2, 278, 22, 3, 2, 2, 2, 279, 280, 9, 10, 2, 2, 280, 24, 3, 2, 2, 2, 281, 282, 9, 11, 2, 2, 282, 26, 3, 2, 2, 2, 283, 284, 9, 12, 2, 2, 284, 28, 3, 2, 2, 2, 285, 286, 9, 13, 2, 2, 286, 30, 3, 2, 2, 2, 287, 288, 9, 14, 2, 2, 288, 32, 3, 2, 2, 2, 289, 290, 9, 15, 2, 2, 290, 34, 3, 2, 2, 2, 291, 292, 9, 16, 2, 2, 292, 36, 3, 2, 2, 2, 293, 294, 9, 17, 2, 2, 294, 38, 3, 2, 2, 2, 295, 296, 9, 18, 2, 2, 296, 40, 3, 2, 2, 2, 297, 298, 9, 19, 2, 2, 298, 42, 3, 2, 2, 2, 299, 300, 9, 20, 2, 2, 300, 44, 3, 2, 2, 2, 301, 302, 9, 21, 2, 2, 302, 46, 3, 2, 2, 2, 303, 304, 9, 22, 2, 2, 304, 48, 3, 2, 2, 2, 305, 306, 9, 23, 2, 2, 306, 50, 3, 2, 2, 2, 307, 308, 9, 24, 2, 2, 308, 52, 3, 2, 2, 2, 309, 310, 9, 25, 2, 2, 310, 54, 3, 2, 2, 2, 311, 312, 9, 26, 2, 2, 312, 56, 3, 2, 2, 2, 313, 314, 9, 27, 2, 2, 314, 58, 3, 2, 2, 2, 315, 316, 9, 28, 2, 2, 316, 60, 3, 2, 2, 2, 317, 318, 7, 97, 2, 2, 318, 62, 3, 2, 2, 2, 319, 320, 9, 29, 2, 2, 320, 64, 3, 2, 2, 2, 321, 322, 9, 30, 2, 2, 322, 66, 3, 2, 2, 2, 323, 329, 7, 36, 2, 2, 324, 325, 7, 94, 2, 2, 325, 328, 11, 2, 2, 2, 326, 328, 10, 31, 2, 2, 327, 324, 3, 2, 2, 2, 327, 326, 3, 2, 2, 2, 328, 331, 3, 2, 2, 2, 329, 327, 3, 2, 2, 2, 329, 330, 3, 2, 2, 2, 330, 332, 3, 2, 2, 2, 331, 329, 3, 2, 2, 2, 332, 333, 7, 36, 2, 2, 333, 68, 3, 2, 2, 2, 334, 340, 7, 41, 2, 2, 335, 336, 7, 94, 2, 2, 336, 339, 11, 2, 2, 2, 337, 339, 10, 32, 2, 2, 338, 335, 3, 2, 2, 2, 338, 337, 3, 2, 2, 2, 339, 342, 3, 2, 2, 2, 340, 338, 3, 2, 2, 2, 340, 341, 3, 2, 2, 2, 341, 343, 3, 2, 2, 2, 342, 340, 3, 2, 2, 2, 343, 344, 7, 41, 2, 2, 344, 70, 3, 2, 2, 2, 345, 353, 7, 98, 2, 2, 346, 347, 7, 94, 2, 2, 347, 352, 11, 2, 2, 2, 348, 349, 7, 98, 2, 2, 349, 352, 7, 98, 2, 2, 350, 352, 10, 33, 2, 2, 351, 346, 3, 2, 2, 2, 351, 348, 3, 2, 2, 2, 351, 350, 3, 2, 2, 2, 352, 355, 3, 2, 2, 2, 353, 351, 3, 2, 2, 2, 353, 354, 3, 2, 2, 2, 354, 356, 3, 2, 2, 2, 355, 353, 3, 2, 2, 2, 356, 357, 7, 98, 2, 2, 357, 72, 3, 2, 2, 2, 358, 359, 5, 47, 24, 2, 359, 360, 5, 43, 22, 2, 360, 361, 5, 49, 25, 2, 361, 362, 5, 17, 9, 2, 362, 74, 3, 2, 2, 2, 363, 364, 5, 19, 10, 2, 364, 365, 5, 9, 5, 2, 365, 366, 5, 31, 16, 2, 366, 367, 5, 45, 23, 2, 367, 368, 5, 17, 9, 2, 368, 76, 3, 2, 2, 2, 369, 370, 5, 19, 10, 2, 370, 371, 5, 25, 13, 2, 371, 372, 5, 17, 9, 2, 372, 373, 5, 31, 16, 2, 373, 374, 5, 15, 8, 2, 374, 78, 3, 2, 2, 2, 375, 376, 5, 19, 10, 2, 376, 377, 5, 25, 13, 2, 377, 378, 5, 17, 9, 2, 378, 379, 5, 31, 16, 2, 379, 380, 5, 15, 8, 2, 380, 381, 5, 61, 31, 2, 381, 382, 5, 11, 6, 2, 382, 383, 5, 57, 29, 2, 383, 384, 5, 61, 31, 2, 384, 385, 5, 25, 13, 2, 385, 386, 5, 15, 8, 2, 386, 80, 3, 2, 2, 2, 387, 388, 7, 46, 2, 2, 388, 82, 3, 2, 2, 2, 389, 390, 7, 60, 2, 2, 390, 84, 3, 2, 2, 2, 391, 392, 7, 60, 2, 2, 392, 393, 7, 60, 2, 2, 393, 86, 3, 2, 2, 2, 394, 395, 7, 38, 2, 2, 395, 88, 3, 2, 2, 2, 396, 397, 7, 38, 2, 2, 397, 398, 7, 38, 2, 2, 398, 90, 3, 2, 2, 2, 399, 400, 7, 44, 2, 2, 400, 92, 3, 2, 2, 2, 401, 402, 7, 42, 2, 2, 402, 94, 3, 2, 2, 2, 403, 404, 7, 43, 2, 2, 404, 96, 3, 2, 2, 2, 405, 406, 7, 93, 2, 2, 406, 98, 3, 2, 2, 2, 407, 408, 7, 95, 2, 2, 408, 100, 3, 2, 2, 2, 409, 410, 5, 11, 6, 2, 410, 414, 7, 41, 2, 2, 411, 413, 4, 50, 51, 2, 412, 411, 3, 2, 2, 2, 413, 416, 3, 2, 2, 2, 414, 412, 3, 2, 2, 2, 414, 415, 3, 2, 2, 2, 415, 417, 3, 2, 2, 2, 416, 414, 3, 2, 2, 2, 417, 418, 7, 41, 2, 2, 418, 102, 3, 2, 2, 2, 419, 420, 5, 17, 9, 2, 420, 421, 5, 69, 35, 2, 421, 104, 3, 2, 2, 2, 422, 424, 7, 47, 2, 2, 423, 422, 3, 2, 2, 2, 423, 424, 3, 2, 2, 2, 424, 426, 3, 2, 2, 2, 425, 427, 5, 65, 33, 2, 426, 425, 3, 2, 2, 2, 427, 428, 3, 2, 2, 2, 428, 426, 3, 2, 2, 2, 428, 429, 3, 2, 2, 2, 429, 430, 3, 2, 2, 2, 430, 432, 7, 48, 2, 2, 431, 433, 5, 65, 33, 2, 432, 431, 3, 2, 2, 2, 433, 434, 3, 2, 2, 2, 434, 432, 3, 2, 2, 2, 434, 435, 3, 2, 2, 2, 435, 448, 3, 2, 2, 2, 436, 440, 5, 17, 9, 2, 437, 439, 7, 47, 2, 2, 438, 437, 3, 2, 2, 2, 439, 442, 3, 2, 2, 2, 440, 438, 3, 2, 2, 2, 440, 441, 3, 2, 2, 2, 441, 444, 3, 2, 2, 2, 442, 440, 3, 2, 2, 2, 443, 445, 5, 65, 33, 2, 444, 443, 3, 2, 2, 2, 445, 446, 3, 2, 2, 2, 446, 444, 3, 2, 2, 2, 446, 447, 3, 2, 2, 2, 447, 449, 3, 2, 2, 2, 448, 436, 3, 2, 2, 2, 448, 449, 3, 2, 2, 2, 449, 106, 3, 2, 2, 2, 450, 452, 7, 47, 2, 2, 451, 450, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 454, 3, 2, 2, 2, 453, 455, 5, 65, 33, 2, 454, 453, 3, 2, 2, 2, 455, 456, 3, 2, 2, 2, 456, 454, 3, 2, 2, 2, 456, 457, 3, 2, 2, 2, 457, 464, 3, 2, 2, 2, 458, 460, 5, 17, 9, 2, 459, 461, 5, 65, 33, 2, 460, 459, 3, 2, 2, 2, 461, 462, 3, 2, 2, 2, 462, 460, 3, 2, 2, 2, 462, 463, 3, 2, 2, 2, 463, 465, 3, 2, 2, 2, 464, 458, 3, 2, 2, 2, 464, 465, 3, 2, 2, 2, 465, 108, 3, 2, 2, 2, 466, 467, 7, 122, 2, 2, 467, 468, 5, 69, 35, 2, 468, 110, 3, 2, 2, 2, 469, 470, 7, 48, 2, 2, 470, 112, 3, 2, 2, 2, 471, 472, 5, 69, 35, 2, 472, 114, 3, 2, 2, 2, 473, 474, 5, 67, 34, 2, 474, 116, 3, 2, 2, 2, 475, 479, 9, 34, 2, 2, 476, 478, 9, 35, 2, 2, 477, 476, 3, 2, 2, 2, 478, 481, 3, 2, 2, 2, 479, 477, 3, 2, 2, 2, 479, 480, 3, 2, 2, 2, 480, 118, 3, 2, 2, 2, 481, 479, 3, 2, 2, 2, 482, 486, 9, 36, 2, 2, 483, 485, 9, 37, 2, 2, 484, 483, 3, 2, 2, 2, 485, 488, 3, 2, 2, 2, 486, 484, 3, 2, 2, 2, 486, 487, 3, 2, 2, 2, 487, 120, 3, 2, 2, 2, 488, 486, 3, 2, 2, 2, 489, 490, 7, 40, 2, 2, 490, 122, 3, 2, 2, 2, 491, 492, 7, 40, 2, 2, 492, 493, 7, 40, 2, 2, 493, 124, 3, 2, 2, 2, 494, 495, 7, 40, 2, 2, 495, 496, 7, 62, 2, 2, 496, 126, 3, 2, 2, 2, 497, 498, 7, 66, 2, 2, 498, 499, 7, 66, 2, 2, 499, 128, 3, 2, 2, 2, 500, 501, 7, 66, 2, 2, 501, 502, 7, 64, 2, 2, 502, 130, 3, 2, 2, 2, 503, 504, 7, 66, 2, 2, 504, 132, 3, 2, 2, 2, 505, 506, 7, 35, 2, 2, 506, 134, 3, 2, 2, 2, 507, 508, 7, 35, 2, 2, 508, 509, 7, 35, 2, 2, 509, 136, 3, 2, 2, 2, 510, 511, 7, 35, 2, 2, 511, 512, 7, 63, 2, 2, 512, 138, 3, 2, 2, 2, 513, 514, 7, 96, 2, 2, 514, 140, 3, 2, 2, 2, 515, 516, 7, 63, 2, 2, 516, 142, 3, 2, 2, 2, 517, 518, 7, 63, 2, 2, 518, 519, 7, 64, 2, 2, 519, 144, 3, 2, 2, 2, 520, 521, 7, 64, 2, 2, 521, 146, 3, 2, 2, 2, 522, 523, 7, 64, 2, 2, 523, 524, 7, 63, 2, 2, 524, 148, 3, 2, 2, 2, 525, 526, 7, 64, 2, 2, 526, 527, 7, 64, 2, 2, 527, 150, 3, 2, 2, 2, 528, 529, 7, 37, 2, 2, 529, 152, 3, 2, 2, 2, 530, 531, 7, 37, 2, 2, 531, 532, 7, 63, 2, 2, 532, 154, 3, 2, 2, 2, 533, 534, 7, 37, 2, 2, 534, 535, 7, 64, 2, 2, 535, 156, 3, 2, 2, 2, 536, 537, 7, 37, 2, 2, 537, 538, 7, 64, 2, 2, 538, 539, 7, 64, 2, 2, 539, 158, 3, 2, 2, 2, 540, 541, 7, 37, 2, 2, 541, 542, 7, 37, 2, 2, 542, 160, 3, 2, 2, 2, 543, 544, 7, 47, 2, 2, 544, 545, 7, 64, 2, 2, 545, 162, 3, 2, 2, 2, 546, 547, 7, 47, 2, 2, 547, 548, 7, 64, 2, 2, 548, 549, 7, 64, 2, 2, 549, 164, 3, 2, 2, 2, 550, 551, 7, 47, 2, 2, 551, 552, 7, 126, 2, 2, 552, 553, 7, 47, 2, 2, 553, 166, 3, 2, 2, 2, 554, 555, 7, 62, 2, 2, 555, 168, 3, 2, 2, 2, 556, 557, 7, 62, 2, 2, 557, 558, 7, 63, 2, 2, 558, 170, 3, 2, 2, 2, 559, 560, 7, 62, 2, 2, 560, 561, 7, 66, 2, 2, 561, 172, 3, 2, 2, 2, 562, 563, 7, 62, 2, 2, 563, 564, 7, 96, 2, 2, 564, 174, 3, 2, 2, 2, 565, 566, 7, 62, 2, 2, 566, 567, 7, 64, 2, 2, 567, 176, 3, 2, 2, 2, 568, 569, 7, 62, 2, 2, 569, 570, 7, 47, 2, 2, 570, 571, 7, 64, 2, 2, 571, 178, 3, 2, 2, 2, 572, 573, 7, 62, 2, 2, 573, 574, 7, 62, 2, 2, 574, 180, 3, 2, 2, 2, 575, 576, 7, 62, 2, 2, 576, 577, 7, 62, 2, 2, 577, 578, 7, 63, 2, 2, 578, 182, 3, 2, 2, 2, 579, 580, 7, 62, 2, 2, 580, 581, 7, 65, 2, 2, 581, 582, 7, 64, 2, 2, 582, 184, 3, 2, 2, 2, 583, 584, 7, 47, 2, 2, 584, 186, 3, 2, 2, 2, 585, 586, 7, 39, 2, 2, 586, 188, 3, 2, 2, 2, 587, 588, 7, 126, 2, 2, 588, 190, 3, 2, 2, 2, 589, 590, 7, 126, 2, 2, 590, 591, 7, 126, 2, 2, 591, 192, 3, 2, 2, 2, 592, 593, 7, 126, 2, 2, 593, 594, 7, 126, 2, 2, 594, 595, 7, 49, 2, 2, 595, 194, 3, 2, 2, 2, 596, 597, 7, 126, 2, 2, 597, 598, 7, 49, 2, 2, 598, 196, 3, 2, 2, 2, 599, 600, 7, 45, 2, 2, 600, 198, 3, 2, 2, 2, 601, 602, 7, 65, 2, 2, 602, 200, 3, 2, 2, 2, 603, 604, 7, 65, 2, 2, 604, 605, 7, 40, 2, 2, 605, 202, 3, 2, 2, 2, 606, 607, 7, 65, 2, 2, 607, 608, 7, 37, 2, 2, 608, 204, 3, 2, 2, 2, 609, 610, 7, 65, 2, 2, 610, 611, 7, 47, 2, 2, 611, 206, 3, 2, 2, 2, 612, 613, 7, 65, 2, 2, 613, 614, 7, 126, 2, 2, 614, 208, 3, 2, 2, 2, 615, 616, 7, 49, 2, 2, 616, 210, 3, 2, 2, 2, 617, 618, 7, 128, 2, 2, 618, 212, 3, 2, 2, 2, 619, 620, 7, 128, 2, 2, 620, 621, 7, 63, 2, 2, 621, 214, 3, 2, 2, 2, 622, 623, 7, 128, 2, 2, 623, 624, 7, 64, 2, 2, 624, 625, 7, 63, 2, 2, 625, 626, 7, 128, 2, 2, 626, 216, 3, 2, 2, 2, 627, 628, 7, 128, 2, 2, 628, 629, 7, 64, 2, 2, 629, 630, 7, 128, 2, 2, 630, 218, 3, 2, 2, 2, 631, 632, 7, 128, 2, 2, 632, 633, 7, 62, 2, 2, 633, 634, 7, 63, 2, 2, 634, 635, 7, 128, 2, 2, 635, 220, 3, 2, 2, 2, 636, 637, 7, 128, 2, 2, 637, 638, 7, 62, 2, 2, 638, 639, 7, 128, 2, 2, 639, 222, 3, 2, 2, 2, 640, 641, 7, 128, 2, 2, 641, 642, 7, 44, 2, 2, 642, 224, 3, 2, 2, 2, 643, 644, 7, 128, 2, 2, 644, 645, 7, 128, 2, 2, 645, 226, 3, 2, 2, 2, 646, 647, 7, 61, 2, 2, 647, 228, 3, 2, 2, 2, 648, 649, 11, 2, 2, 2, 649, 230, 3, 2, 2, 2, 25, 2, 234, 244, 258, 327, 329, 338, 340, 351, 353, 414, 423, 428, 434, 440, 446, 448, 451, 456, 462, 464, 479, 486, 3, 2, 3, 2] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 84, 642, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 7, 31, 294, 10, 31, 12, 31, 14, 31, 297, 11, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 7, 32, 305, 10, 32, 12, 32, 14, 32, 308, 11, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 7, 33, 318, 10, 33, 12, 33, 14, 33, 321, 11, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 7, 34, 329, 10, 34, 12, 34, 14, 34, 332, 11, 34, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 7, 35, 341, 10, 35, 12, 35, 14, 35, 344, 11, 35, 3, 36, 6, 36, 347, 10, 36, 13, 36, 14, 36, 348, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 7, 51, 405, 10, 51, 12, 51, 14, 51, 408, 11, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 5, 53, 416, 10, 53, 3, 53, 6, 53, 419, 10, 53, 13, 53, 14, 53, 420, 3, 53, 3, 53, 6, 53, 425, 10, 53, 13, 53, 14, 53, 426, 3, 53, 3, 53, 7, 53, 431, 10, 53, 12, 53, 14, 53, 434, 11, 53, 3, 53, 6, 53, 437, 10, 53, 13, 53, 14, 53, 438, 5, 53, 441, 10, 53, 3, 54, 5, 54, 444, 10, 54, 3, 54, 6, 54, 447, 10, 54, 13, 54, 14, 54, 448, 3, 54, 3, 54, 6, 54, 453, 10, 54, 13, 54, 14, 54, 454, 5, 54, 457, 10, 54, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 7, 59, 470, 10, 59, 12, 59, 14, 59, 473, 11, 59, 3, 60, 3, 60, 7, 60, 477, 10, 60, 12, 60, 14, 60, 480, 11, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 82, 3, 83, 3, 83, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 96, 3, 97, 3, 97, 3, 97, 3, 97, 3, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 101, 3, 102, 3, 102, 3, 102, 3, 103, 3, 103, 3, 103, 3, 104, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 107, 3, 108, 3, 108, 3, 108, 3, 108, 3, 108, 3, 109, 3, 109, 3, 109, 3, 109, 3, 110, 3, 110, 3, 110, 3, 110, 3, 110, 3, 111, 3, 111, 3, 111, 3, 111, 3, 112, 3, 112, 3, 112, 3, 113, 3, 113, 3, 113, 3, 114, 3, 114, 3, 115, 3, 115, 2, 2, 116, 3, 2, 5, 2, 7, 2, 9, 2, 11, 2, 13, 2, 15, 2, 17, 2, 19, 2, 21, 2, 23, 2, 25, 2, 27, 2, 29, 2, 31, 2, 33, 2, 35, 2, 37, 2, 39, 2, 41, 2, 43, 2, 45, 2, 47, 2, 49, 2, 51, 2, 53, 2, 55, 2, 57, 2, 59, 2, 61, 2, 63, 2, 65, 2, 67, 3, 69, 4, 71, 5, 73, 6, 75, 7, 77, 8, 79, 9, 81, 10, 83, 11, 85, 12, 87, 13, 89, 14, 91, 15, 93, 16, 95, 17, 97, 18, 99, 19, 101, 20, 103, 21, 105, 22, 107, 23, 109, 24, 111, 25, 113, 26, 115, 27, 117, 28, 119, 29, 121, 30, 123, 31, 125, 32, 127, 33, 129, 34, 131, 35, 133, 36, 135, 37, 137, 38, 139, 39, 141, 40, 143, 41, 145, 42, 147, 43, 149, 44, 151, 45, 153, 46, 155, 47, 157, 48, 159, 49, 161, 50, 163, 51, 165, 52, 167, 53, 169, 54, 171, 55, 173, 56, 175, 57, 177, 58, 179, 59, 181, 60, 183, 61, 185, 62, 187, 63, 189, 64, 191, 65, 193, 66, 195, 67, 197, 68, 199, 69, 201, 70, 203, 71, 205, 72, 207, 73, 209, 74, 211, 75, 213, 76, 215, 77, 217, 78, 219, 79, 221, 80, 223, 81, 225, 82, 227, 83, 229, 84, 3, 2, 39, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 4, 2, 50, 59, 67, 72, 3, 2, 50, 59, 4, 2, 36, 36, 94, 94, 4, 2, 41, 41, 94, 94, 4, 2, 94, 94, 98, 98, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 6, 2, 67, 92, 97, 97, 99, 124, 163, 1, 7, 2, 50, 59, 67, 92, 97, 97, 99, 124, 163, 1, 2, 632, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 157, 3, 2, 2, 2, 2, 159, 3, 2, 2, 2, 2, 161, 3, 2, 2, 2, 2, 163, 3, 2, 2, 2, 2, 165, 3, 2, 2, 2, 2, 167, 3, 2, 2, 2, 2, 169, 3, 2, 2, 2, 2, 171, 3, 2, 2, 2, 2, 173, 3, 2, 2, 2, 2, 175, 3, 2, 2, 2, 2, 177, 3, 2, 2, 2, 2, 179, 3, 2, 2, 2, 2, 181, 3, 2, 2, 2, 2, 183, 3, 2, 2, 2, 2, 185, 3, 2, 2, 2, 2, 187, 3, 2, 2, 2, 2, 189, 3, 2, 2, 2, 2, 191, 3, 2, 2, 2, 2, 193, 3, 2, 2, 2, 2, 195, 3, 2, 2, 2, 2, 197, 3, 2, 2, 2, 2, 199, 3, 2, 2, 2, 2, 201, 3, 2, 2, 2, 2, 203, 3, 2, 2, 2, 2, 205, 3, 2, 2, 2, 2, 207, 3, 2, 2, 2, 2, 209, 3, 2, 2, 2, 2, 211, 3, 2, 2, 2, 2, 213, 3, 2, 2, 2, 2, 215, 3, 2, 2, 2, 2, 217, 3, 2, 2, 2, 2, 219, 3, 2, 2, 2, 2, 221, 3, 2, 2, 2, 2, 223, 3, 2, 2, 2, 2, 225, 3, 2, 2, 2, 2, 227, 3, 2, 2, 2, 2, 229, 3, 2, 2, 2, 3, 231, 3, 2, 2, 2, 5, 233, 3, 2, 2, 2, 7, 235, 3, 2, 2, 2, 9, 237, 3, 2, 2, 2, 11, 239, 3, 2, 2, 2, 13, 241, 3, 2, 2, 2, 15, 243, 3, 2, 2, 2, 17, 245, 3, 2, 2, 2, 19, 247, 3, 2, 2, 2, 21, 249, 3, 2, 2, 2, 23, 251, 3, 2, 2, 2, 25, 253, 3, 2, 2, 2, 27, 255, 3, 2, 2, 2, 29, 257, 3, 2, 2, 2, 31, 259, 3, 2, 2, 2, 33, 261, 3, 2, 2, 2, 35, 263, 3, 2, 2, 2, 37, 265, 3, 2, 2, 2, 39, 267, 3, 2, 2, 2, 41, 269, 3, 2, 2, 2, 43, 271, 3, 2, 2, 2, 45, 273, 3, 2, 2, 2, 47, 275, 3, 2, 2, 2, 49, 277, 3, 2, 2, 2, 51, 279, 3, 2, 2, 2, 53, 281, 3, 2, 2, 2, 55, 283, 3, 2, 2, 2, 57, 285, 3, 2, 2, 2, 59, 287, 3, 2, 2, 2, 61, 289, 3, 2, 2, 2, 63, 300, 3, 2, 2, 2, 65, 311, 3, 2, 2, 2, 67, 324, 3, 2, 2, 2, 69, 336, 3, 2, 2, 2, 71, 346, 3, 2, 2, 2, 73, 350, 3, 2, 2, 2, 75, 355, 3, 2, 2, 2, 77, 361, 3, 2, 2, 2, 79, 367, 3, 2, 2, 2, 81, 379, 3, 2, 2, 2, 83, 381, 3, 2, 2, 2, 85, 383, 3, 2, 2, 2, 87, 386, 3, 2, 2, 2, 89, 388, 3, 2, 2, 2, 91, 391, 3, 2, 2, 2, 93, 393, 3, 2, 2, 2, 95, 395, 3, 2, 2, 2, 97, 397, 3, 2, 2, 2, 99, 399, 3, 2, 2, 2, 101, 401, 3, 2, 2, 2, 103, 411, 3, 2, 2, 2, 105, 415, 3, 2, 2, 2, 107, 443, 3, 2, 2, 2, 109, 458, 3, 2, 2, 2, 111, 461, 3, 2, 2, 2, 113, 463, 3, 2, 2, 2, 115, 465, 3, 2, 2, 2, 117, 467, 3, 2, 2, 2, 119, 474, 3, 2, 2, 2, 121, 481, 3, 2, 2, 2, 123, 483, 3, 2, 2, 2, 125, 486, 3, 2, 2, 2, 127, 489, 3, 2, 2, 2, 129, 492, 3, 2, 2, 2, 131, 495, 3, 2, 2, 2, 133, 497, 3, 2, 2, 2, 135, 499, 3, 2, 2, 2, 137, 502, 3, 2, 2, 2, 139, 505, 3, 2, 2, 2, 141, 507, 3, 2, 2, 2, 143, 509, 3, 2, 2, 2, 145, 512, 3, 2, 2, 2, 147, 514, 3, 2, 2, 2, 149, 517, 3, 2, 2, 2, 151, 520, 3, 2, 2, 2, 153, 522, 3, 2, 2, 2, 155, 525, 3, 2, 2, 2, 157, 528, 3, 2, 2, 2, 159, 532, 3, 2, 2, 2, 161, 535, 3, 2, 2, 2, 163, 538, 3, 2, 2, 2, 165, 542, 3, 2, 2, 2, 167, 546, 3, 2, 2, 2, 169, 548, 3, 2, 2, 2, 171, 551, 3, 2, 2, 2, 173, 554, 3, 2, 2, 2, 175, 557, 3, 2, 2, 2, 177, 560, 3, 2, 2, 2, 179, 564, 3, 2, 2, 2, 181, 567, 3, 2, 2, 2, 183, 571, 3, 2, 2, 2, 185, 575, 3, 2, 2, 2, 187, 577, 3, 2, 2, 2, 189, 579, 3, 2, 2, 2, 191, 581, 3, 2, 2, 2, 193, 584, 3, 2, 2, 2, 195, 588, 3, 2, 2, 2, 197, 591, 3, 2, 2, 2, 199, 593, 3, 2, 2, 2, 201, 595, 3, 2, 2, 2, 203, 598, 3, 2, 2, 2, 205, 601, 3, 2, 2, 2, 207, 604, 3, 2, 2, 2, 209, 607, 3, 2, 2, 2, 211, 609, 3, 2, 2, 2, 213, 611, 3, 2, 2, 2, 215, 614, 3, 2, 2, 2, 217, 619, 3, 2, 2, 2, 219, 623, 3, 2, 2, 2, 221, 628, 3, 2, 2, 2, 223, 632, 3, 2, 2, 2, 225, 635, 3, 2, 2, 2, 227, 638, 3, 2, 2, 2, 229, 640, 3, 2, 2, 2, 231, 232, 9, 2, 2, 2, 232, 4, 3, 2, 2, 2, 233, 234, 9, 3, 2, 2, 234, 6, 3, 2, 2, 2, 235, 236, 9, 4, 2, 2, 236, 8, 3, 2, 2, 2, 237, 238, 9, 5, 2, 2, 238, 10, 3, 2, 2, 2, 239, 240, 9, 6, 2, 2, 240, 12, 3, 2, 2, 2, 241, 242, 9, 7, 2, 2, 242, 14, 3, 2, 2, 2, 243, 244, 9, 8, 2, 2, 244, 16, 3, 2, 2, 2, 245, 246, 9, 9, 2, 2, 246, 18, 3, 2, 2, 2, 247, 248, 9, 10, 2, 2, 248, 20, 3, 2, 2, 2, 249, 250, 9, 11, 2, 2, 250, 22, 3, 2, 2, 2, 251, 252, 9, 12, 2, 2, 252, 24, 3, 2, 2, 2, 253, 254, 9, 13, 2, 2, 254, 26, 3, 2, 2, 2, 255, 256, 9, 14, 2, 2, 256, 28, 3, 2, 2, 2, 257, 258, 9, 15, 2, 2, 258, 30, 3, 2, 2, 2, 259, 260, 9, 16, 2, 2, 260, 32, 3, 2, 2, 2, 261, 262, 9, 17, 2, 2, 262, 34, 3, 2, 2, 2, 263, 264, 9, 18, 2, 2, 264, 36, 3, 2, 2, 2, 265, 266, 9, 19, 2, 2, 266, 38, 3, 2, 2, 2, 267, 268, 9, 20, 2, 2, 268, 40, 3, 2, 2, 2, 269, 270, 9, 21, 2, 2, 270, 42, 3, 2, 2, 2, 271, 272, 9, 22, 2, 2, 272, 44, 3, 2, 2, 2, 273, 274, 9, 23, 2, 2, 274, 46, 3, 2, 2, 2, 275, 276, 9, 24, 2, 2, 276, 48, 3, 2, 2, 2, 277, 278, 9, 25, 2, 2, 278, 50, 3, 2, 2, 2, 279, 280, 9, 26, 2, 2, 280, 52, 3, 2, 2, 2, 281, 282, 9, 27, 2, 2, 282, 54, 3, 2, 2, 2, 283, 284, 7, 97, 2, 2, 284, 56, 3, 2, 2, 2, 285, 286, 9, 28, 2, 2, 286, 58, 3, 2, 2, 2, 287, 288, 9, 29, 2, 2, 288, 60, 3, 2, 2, 2, 289, 295, 7, 36, 2, 2, 290, 291, 7, 94, 2, 2, 291, 294, 11, 2, 2, 2, 292, 294, 10, 30, 2, 2, 293, 290, 3, 2, 2, 2, 293, 292, 3, 2, 2, 2, 294, 297, 3, 2, 2, 2, 295, 293, 3, 2, 2, 2, 295, 296, 3, 2, 2, 2, 296, 298, 3, 2, 2, 2, 297, 295, 3, 2, 2, 2, 298, 299, 7, 36, 2, 2, 299, 62, 3, 2, 2, 2, 300, 306, 7, 41, 2, 2, 301, 302, 7, 94, 2, 2, 302, 305, 11, 2, 2, 2, 303, 305, 10, 31, 2, 2, 304, 301, 3, 2, 2, 2, 304, 303, 3, 2, 2, 2, 305, 308, 3, 2, 2, 2, 306, 304, 3, 2, 2, 2, 306, 307, 3, 2, 2, 2, 307, 309, 3, 2, 2, 2, 308, 306, 3, 2, 2, 2, 309, 310, 7, 41, 2, 2, 310, 64, 3, 2, 2, 2, 311, 319, 7, 98, 2, 2, 312, 313, 7, 94, 2, 2, 313, 318, 11, 2, 2, 2, 314, 315, 7, 98, 2, 2, 315, 318, 7, 98, 2, 2, 316, 318, 10, 32, 2, 2, 317, 312, 3, 2, 2, 2, 317, 314, 3, 2, 2, 2, 317, 316, 3, 2, 2, 2, 318, 321, 3, 2, 2, 2, 319, 317, 3, 2, 2, 2, 319, 320, 3, 2, 2, 2, 320, 322, 3, 2, 2, 2, 321, 319, 3, 2, 2, 2, 322, 323, 7, 98, 2, 2, 323, 66, 3, 2, 2, 2, 324, 325, 7, 49, 2, 2, 325, 326, 7, 44, 2, 2, 326, 330, 3, 2, 2, 2, 327, 329, 11, 2, 2, 2, 328, 327, 3, 2, 2, 2, 329, 332, 3, 2, 2, 2, 330, 328, 3, 2, 2, 2, 330, 331, 3, 2, 2, 2, 331, 333, 3, 2, 2, 2, 332, 330, 3, 2, 2, 2, 333, 334, 7, 44, 2, 2, 334, 335, 7, 49, 2, 2, 335, 68, 3, 2, 2, 2, 336, 337, 7, 49, 2, 2, 337, 338, 7, 49, 2, 2, 338, 342, 3, 2, 2, 2, 339, 341, 10, 33, 2, 2, 340, 339, 3, 2, 2, 2, 341, 344, 3, 2, 2, 2, 342, 340, 3, 2, 2, 2, 342, 343, 3, 2, 2, 2, 343, 70, 3, 2, 2, 2, 344, 342, 3, 2, 2, 2, 345, 347, 9, 34, 2, 2, 346, 345, 3, 2, 2, 2, 347, 348, 3, 2, 2, 2, 348, 346, 3, 2, 2, 2, 348, 349, 3, 2, 2, 2, 349, 72, 3, 2, 2, 2, 350, 351, 5, 41, 21, 2, 351, 352, 5, 37, 19, 2, 352, 353, 5, 43, 22, 2, 353, 354, 5, 11, 6, 2, 354, 74, 3, 2, 2, 2, 355, 356, 5, 13, 7, 2, 356, 357, 5, 3, 2, 2, 357, 358, 5, 25, 13, 2, 358, 359, 5, 39, 20, 2, 359, 360, 5, 11, 6, 2, 360, 76, 3, 2, 2, 2, 361, 362, 5, 13, 7, 2, 362, 363, 5, 19, 10, 2, 363, 364, 5, 11, 6, 2, 364, 365, 5, 25, 13, 2, 365, 366, 5, 9, 5, 2, 366, 78, 3, 2, 2, 2, 367, 368, 5, 13, 7, 2, 368, 369, 5, 19, 10, 2, 369, 370, 5, 11, 6, 2, 370, 371, 5, 25, 13, 2, 371, 372, 5, 9, 5, 2, 372, 373, 5, 55, 28, 2, 373, 374, 5, 5, 3, 2, 374, 375, 5, 51, 26, 2, 375, 376, 5, 55, 28, 2, 376, 377, 5, 19, 10, 2, 377, 378, 5, 9, 5, 2, 378, 80, 3, 2, 2, 2, 379, 380, 7, 46, 2, 2, 380, 82, 3, 2, 2, 2, 381, 382, 7, 60, 2, 2, 382, 84, 3, 2, 2, 2, 383, 384, 7, 60, 2, 2, 384, 385, 7, 60, 2, 2, 385, 86, 3, 2, 2, 2, 386, 387, 7, 38, 2, 2, 387, 88, 3, 2, 2, 2, 388, 389, 7, 38, 2, 2, 389, 390, 7, 38, 2, 2, 390, 90, 3, 2, 2, 2, 391, 392, 7, 44, 2, 2, 392, 92, 3, 2, 2, 2, 393, 394, 7, 42, 2, 2, 394, 94, 3, 2, 2, 2, 395, 396, 7, 43, 2, 2, 396, 96, 3, 2, 2, 2, 397, 398, 7, 93, 2, 2, 398, 98, 3, 2, 2, 2, 399, 400, 7, 95, 2, 2, 400, 100, 3, 2, 2, 2, 401, 402, 5, 5, 3, 2, 402, 406, 7, 41, 2, 2, 403, 405, 4, 50, 51, 2, 404, 403, 3, 2, 2, 2, 405, 408, 3, 2, 2, 2, 406, 404, 3, 2, 2, 2, 406, 407, 3, 2, 2, 2, 407, 409, 3, 2, 2, 2, 408, 406, 3, 2, 2, 2, 409, 410, 7, 41, 2, 2, 410, 102, 3, 2, 2, 2, 411, 412, 5, 11, 6, 2, 412, 413, 5, 63, 32, 2, 413, 104, 3, 2, 2, 2, 414, 416, 7, 47, 2, 2, 415, 414, 3, 2, 2, 2, 415, 416, 3, 2, 2, 2, 416, 418, 3, 2, 2, 2, 417, 419, 5, 59, 30, 2, 418, 417, 3, 2, 2, 2, 419, 420, 3, 2, 2, 2, 420, 418, 3, 2, 2, 2, 420, 421, 3, 2, 2, 2, 421, 422, 3, 2, 2, 2, 422, 424, 7, 48, 2, 2, 423, 425, 5, 59, 30, 2, 424, 423, 3, 2, 2, 2, 425, 426, 3, 2, 2, 2, 426, 424, 3, 2, 2, 2, 426, 427, 3, 2, 2, 2, 427, 440, 3, 2, 2, 2, 428, 432, 5, 11, 6, 2, 429, 431, 7, 47, 2, 2, 430, 429, 3, 2, 2, 2, 431, 434, 3, 2, 2, 2, 432, 430, 3, 2, 2, 2, 432, 433, 3, 2, 2, 2, 433, 436, 3, 2, 2, 2, 434, 432, 3, 2, 2, 2, 435, 437, 5, 59, 30, 2, 436, 435, 3, 2, 2, 2, 437, 438, 3, 2, 2, 2, 438, 436, 3, 2, 2, 2, 438, 439, 3, 2, 2, 2, 439, 441, 3, 2, 2, 2, 440, 428, 3, 2, 2, 2, 440, 441, 3, 2, 2, 2, 441, 106, 3, 2, 2, 2, 442, 444, 7, 47, 2, 2, 443, 442, 3, 2, 2, 2, 443, 444, 3, 2, 2, 2, 444, 446, 3, 2, 2, 2, 445, 447, 5, 59, 30, 2, 446, 445, 3, 2, 2, 2, 447, 448, 3, 2, 2, 2, 448, 446, 3, 2, 2, 2, 448, 449, 3, 2, 2, 2, 449, 456, 3, 2, 2, 2, 450, 452, 5, 11, 6, 2, 451, 453, 5, 59, 30, 2, 452, 451, 3, 2, 2, 2, 453, 454, 3, 2, 2, 2, 454, 452, 3, 2, 2, 2, 454, 455, 3, 2, 2, 2, 455, 457, 3, 2, 2, 2, 456, 450, 3, 2, 2, 2, 456, 457, 3, 2, 2, 2, 457, 108, 3, 2, 2, 2, 458, 459, 7, 122, 2, 2, 459, 460, 5, 63, 32, 2, 460, 110, 3, 2, 2, 2, 461, 462, 7, 48, 2, 2, 462, 112, 3, 2, 2, 2, 463, 464, 5, 63, 32, 2, 464, 114, 3, 2, 2, 2, 465, 466, 5, 61, 31, 2, 466, 116, 3, 2, 2, 2, 467, 471, 9, 35, 2, 2, 468, 470, 9, 36, 2, 2, 469, 468, 3, 2, 2, 2, 470, 473, 3, 2, 2, 2, 471, 469, 3, 2, 2, 2, 471, 472, 3, 2, 2, 2, 472, 118, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 474, 478, 9, 37, 2, 2, 475, 477, 9, 38, 2, 2, 476, 475, 3, 2, 2, 2, 477, 480, 3, 2, 2, 2, 478, 476, 3, 2, 2, 2, 478, 479, 3, 2, 2, 2, 479, 120, 3, 2, 2, 2, 480, 478, 3, 2, 2, 2, 481, 482, 7, 40, 2, 2, 482, 122, 3, 2, 2, 2, 483, 484, 7, 40, 2, 2, 484, 485, 7, 40, 2, 2, 485, 124, 3, 2, 2, 2, 486, 487, 7, 40, 2, 2, 487, 488, 7, 62, 2, 2, 488, 126, 3, 2, 2, 2, 489, 490, 7, 66, 2, 2, 490, 491, 7, 66, 2, 2, 491, 128, 3, 2, 2, 2, 492, 493, 7, 66, 2, 2, 493, 494, 7, 64, 2, 2, 494, 130, 3, 2, 2, 2, 495, 496, 7, 66, 2, 2, 496, 132, 3, 2, 2, 2, 497, 498, 7, 35, 2, 2, 498, 134, 3, 2, 2, 2, 499, 500, 7, 35, 2, 2, 500, 501, 7, 35, 2, 2, 501, 136, 3, 2, 2, 2, 502, 503, 7, 35, 2, 2, 503, 504, 7, 63, 2, 2, 504, 138, 3, 2, 2, 2, 505, 506, 7, 96, 2, 2, 506, 140, 3, 2, 2, 2, 507, 508, 7, 63, 2, 2, 508, 142, 3, 2, 2, 2, 509, 510, 7, 63, 2, 2, 510, 511, 7, 64, 2, 2, 511, 144, 3, 2, 2, 2, 512, 513, 7, 64, 2, 2, 513, 146, 3, 2, 2, 2, 514, 515, 7, 64, 2, 2, 515, 516, 7, 63, 2, 2, 516, 148, 3, 2, 2, 2, 517, 518, 7, 64, 2, 2, 518, 519, 7, 64, 2, 2, 519, 150, 3, 2, 2, 2, 520, 521, 7, 37, 2, 2, 521, 152, 3, 2, 2, 2, 522, 523, 7, 37, 2, 2, 523, 524, 7, 63, 2, 2, 524, 154, 3, 2, 2, 2, 525, 526, 7, 37, 2, 2, 526, 527, 7, 64, 2, 2, 527, 156, 3, 2, 2, 2, 528, 529, 7, 37, 2, 2, 529, 530, 7, 64, 2, 2, 530, 531, 7, 64, 2, 2, 531, 158, 3, 2, 2, 2, 532, 533, 7, 37, 2, 2, 533, 534, 7, 37, 2, 2, 534, 160, 3, 2, 2, 2, 535, 536, 7, 47, 2, 2, 536, 537, 7, 64, 2, 2, 537, 162, 3, 2, 2, 2, 538, 539, 7, 47, 2, 2, 539, 540, 7, 64, 2, 2, 540, 541, 7, 64, 2, 2, 541, 164, 3, 2, 2, 2, 542, 543, 7, 47, 2, 2, 543, 544, 7, 126, 2, 2, 544, 545, 7, 47, 2, 2, 545, 166, 3, 2, 2, 2, 546, 547, 7, 62, 2, 2, 547, 168, 3, 2, 2, 2, 548, 549, 7, 62, 2, 2, 549, 550, 7, 63, 2, 2, 550, 170, 3, 2, 2, 2, 551, 552, 7, 62, 2, 2, 552, 553, 7, 66, 2, 2, 553, 172, 3, 2, 2, 2, 554, 555, 7, 62, 2, 2, 555, 556, 7, 96, 2, 2, 556, 174, 3, 2, 2, 2, 557, 558, 7, 62, 2, 2, 558, 559, 7, 64, 2, 2, 559, 176, 3, 2, 2, 2, 560, 561, 7, 62, 2, 2, 561, 562, 7, 47, 2, 2, 562, 563, 7, 64, 2, 2, 563, 178, 3, 2, 2, 2, 564, 565, 7, 62, 2, 2, 565, 566, 7, 62, 2, 2, 566, 180, 3, 2, 2, 2, 567, 568, 7, 62, 2, 2, 568, 569, 7, 62, 2, 2, 569, 570, 7, 63, 2, 2, 570, 182, 3, 2, 2, 2, 571, 572, 7, 62, 2, 2, 572, 573, 7, 65, 2, 2, 573, 574, 7, 64, 2, 2, 574, 184, 3, 2, 2, 2, 575, 576, 7, 47, 2, 2, 576, 186, 3, 2, 2, 2, 577, 578, 7, 39, 2, 2, 578, 188, 3, 2, 2, 2, 579, 580, 7, 126, 2, 2, 580, 190, 3, 2, 2, 2, 581, 582, 7, 126, 2, 2, 582, 583, 7, 126, 2, 2, 583, 192, 3, 2, 2, 2, 584, 585, 7, 126, 2, 2, 585, 586, 7, 126, 2, 2, 586, 587, 7, 49, 2, 2, 587, 194, 3, 2, 2, 2, 588, 589, 7, 126, 2, 2, 589, 590, 7, 49, 2, 2, 590, 196, 3, 2, 2, 2, 591, 592, 7, 45, 2, 2, 592, 198, 3, 2, 2, 2, 593, 594, 7, 65, 2, 2, 594, 200, 3, 2, 2, 2, 595, 596, 7, 65, 2, 2, 596, 597, 7, 40, 2, 2, 597, 202, 3, 2, 2, 2, 598, 599, 7, 65, 2, 2, 599, 600, 7, 37, 2, 2, 600, 204, 3, 2, 2, 2, 601, 602, 7, 65, 2, 2, 602, 603, 7, 47, 2, 2, 603, 206, 3, 2, 2, 2, 604, 605, 7, 65, 2, 2, 605, 606, 7, 126, 2, 2, 606, 208, 3, 2, 2, 2, 607, 608, 7, 49, 2, 2, 608, 210, 3, 2, 2, 2, 609, 610, 7, 128, 2, 2, 610, 212, 3, 2, 2, 2, 611, 612, 7, 128, 2, 2, 612, 613, 7, 63, 2, 2, 613, 214, 3, 2, 2, 2, 614, 615, 7, 128, 2, 2, 615, 616, 7, 64, 2, 2, 616, 617, 7, 63, 2, 2, 617, 618, 7, 128, 2, 2, 618, 216, 3, 2, 2, 2, 619, 620, 7, 128, 2, 2, 620, 621, 7, 64, 2, 2, 621, 622, 7, 128, 2, 2, 622, 218, 3, 2, 2, 2, 623, 624, 7, 128, 2, 2, 624, 625, 7, 62, 2, 2, 625, 626, 7, 63, 2, 2, 626, 627, 7, 128, 2, 2, 627, 220, 3, 2, 2, 2, 628, 629, 7, 128, 2, 2, 629, 630, 7, 62, 2, 2, 630, 631, 7, 128, 2, 2, 631, 222, 3, 2, 2, 2, 632, 633, 7, 128, 2, 2, 633, 634, 7, 44, 2, 2, 634, 224, 3, 2, 2, 2, 635, 636, 7, 128, 2, 2, 636, 637, 7, 128, 2, 2, 637, 226, 3, 2, 2, 2, 638, 639, 7, 61, 2, 2, 639, 228, 3, 2, 2, 2, 640, 641, 11, 2, 2, 2, 641, 230, 3, 2, 2, 2, 25, 2, 293, 295, 304, 306, 317, 319, 330, 342, 348, 406, 415, 420, 426, 432, 438, 440, 443, 448, 454, 456, 471, 478, 2] \ No newline at end of file diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.js b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.js index 711cdeec9..193df4859 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.js +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.js @@ -5,7 +5,7 @@ var antlr4 = require('antlr4/index'); var serializedATN = ["\u0003\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964", - "\u0002T\u028a\b\u0001\u0004\u0002\t\u0002\u0004\u0003\t\u0003\u0004", + "\u0002T\u0282\b\u0001\u0004\u0002\t\u0002\u0004\u0003\t\u0003\u0004", "\u0004\t\u0004\u0004\u0005\t\u0005\u0004\u0006\t\u0006\u0004\u0007\t", "\u0007\u0004\b\t\b\u0004\t\t\t\u0004\n\t\n\u0004\u000b\t\u000b\u0004", "\f\t\f\u0004\r\t\r\u0004\u000e\t\u000e\u0004\u000f\t\u000f\u0004\u0010", @@ -25,387 +25,382 @@ var serializedATN = ["\u0003\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964", "[\t[\u0004\\\t\\\u0004]\t]\u0004^\t^\u0004_\t_\u0004`\t`\u0004a\ta\u0004", "b\tb\u0004c\tc\u0004d\td\u0004e\te\u0004f\tf\u0004g\tg\u0004h\th\u0004", "i\ti\u0004j\tj\u0004k\tk\u0004l\tl\u0004m\tm\u0004n\tn\u0004o\to\u0004", - "p\tp\u0004q\tq\u0004r\tr\u0004s\ts\u0003\u0002\u0006\u0002\u00e9\n\u0002", - "\r\u0002\u000e\u0002\u00ea\u0003\u0002\u0003\u0002\u0003\u0003\u0003", - "\u0003\u0003\u0003\u0003\u0003\u0007\u0003\u00f3\n\u0003\f\u0003\u000e", - "\u0003\u00f6\u000b\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003", - "\u0003\u0003\u0003\u0004\u0003\u0004\u0003\u0004\u0003\u0004\u0007\u0004", - "\u0101\n\u0004\f\u0004\u000e\u0004\u0104\u000b\u0004\u0003\u0004\u0003", - "\u0004\u0003\u0004\u0003\u0004\u0003\u0005\u0003\u0005\u0003\u0006\u0003", - "\u0006\u0003\u0007\u0003\u0007\u0003\b\u0003\b\u0003\t\u0003\t\u0003", - "\n\u0003\n\u0003\u000b\u0003\u000b\u0003\f\u0003\f\u0003\r\u0003\r\u0003", - "\u000e\u0003\u000e\u0003\u000f\u0003\u000f\u0003\u0010\u0003\u0010\u0003", - "\u0011\u0003\u0011\u0003\u0012\u0003\u0012\u0003\u0013\u0003\u0013\u0003", - "\u0014\u0003\u0014\u0003\u0015\u0003\u0015\u0003\u0016\u0003\u0016\u0003", - "\u0017\u0003\u0017\u0003\u0018\u0003\u0018\u0003\u0019\u0003\u0019\u0003", - "\u001a\u0003\u001a\u0003\u001b\u0003\u001b\u0003\u001c\u0003\u001c\u0003", - "\u001d\u0003\u001d\u0003\u001e\u0003\u001e\u0003\u001f\u0003\u001f\u0003", - " \u0003 \u0003!\u0003!\u0003\"\u0003\"\u0003\"\u0003\"\u0007\"\u0148", - "\n\"\f\"\u000e\"\u014b\u000b\"\u0003\"\u0003\"\u0003#\u0003#\u0003#", - "\u0003#\u0007#\u0153\n#\f#\u000e#\u0156\u000b#\u0003#\u0003#\u0003$", - "\u0003$\u0003$\u0003$\u0003$\u0003$\u0007$\u0160\n$\f$\u000e$\u0163", - "\u000b$\u0003$\u0003$\u0003%\u0003%\u0003%\u0003%\u0003%\u0003&\u0003", - "&\u0003&\u0003&\u0003&\u0003&\u0003\'\u0003\'\u0003\'\u0003\'\u0003", - "\'\u0003\'\u0003(\u0003(\u0003(\u0003(\u0003(\u0003(\u0003(\u0003(\u0003", - "(\u0003(\u0003(\u0003(\u0003)\u0003)\u0003*\u0003*\u0003+\u0003+\u0003", - "+\u0003,\u0003,\u0003-\u0003-\u0003-\u0003.\u0003.\u0003/\u0003/\u0003", - "0\u00030\u00031\u00031\u00032\u00032\u00033\u00033\u00033\u00073\u019d", - "\n3\f3\u000e3\u01a0\u000b3\u00033\u00033\u00034\u00034\u00034\u0003", - "5\u00055\u01a8\n5\u00035\u00065\u01ab\n5\r5\u000e5\u01ac\u00035\u0003", - "5\u00065\u01b1\n5\r5\u000e5\u01b2\u00035\u00035\u00075\u01b7\n5\f5\u000e", - "5\u01ba\u000b5\u00035\u00065\u01bd\n5\r5\u000e5\u01be\u00055\u01c1\n", - "5\u00036\u00056\u01c4\n6\u00036\u00066\u01c7\n6\r6\u000e6\u01c8\u0003", - "6\u00036\u00066\u01cd\n6\r6\u000e6\u01ce\u00056\u01d1\n6\u00037\u0003", - "7\u00037\u00038\u00038\u00039\u00039\u0003:\u0003:\u0003;\u0003;\u0007", - ";\u01de\n;\f;\u000e;\u01e1\u000b;\u0003<\u0003<\u0007<\u01e5\n<\f<\u000e", - "<\u01e8\u000b<\u0003=\u0003=\u0003>\u0003>\u0003>\u0003?\u0003?\u0003", - "?\u0003@\u0003@\u0003@\u0003A\u0003A\u0003A\u0003B\u0003B\u0003C\u0003", - "C\u0003D\u0003D\u0003D\u0003E\u0003E\u0003E\u0003F\u0003F\u0003G\u0003", - "G\u0003H\u0003H\u0003H\u0003I\u0003I\u0003J\u0003J\u0003J\u0003K\u0003", - "K\u0003K\u0003L\u0003L\u0003M\u0003M\u0003M\u0003N\u0003N\u0003N\u0003", - "O\u0003O\u0003O\u0003O\u0003P\u0003P\u0003P\u0003Q\u0003Q\u0003Q\u0003", - "R\u0003R\u0003R\u0003R\u0003S\u0003S\u0003S\u0003S\u0003T\u0003T\u0003", - "U\u0003U\u0003U\u0003V\u0003V\u0003V\u0003W\u0003W\u0003W\u0003X\u0003", - "X\u0003X\u0003Y\u0003Y\u0003Y\u0003Y\u0003Z\u0003Z\u0003Z\u0003[\u0003", - "[\u0003[\u0003[\u0003\\\u0003\\\u0003\\\u0003\\\u0003]\u0003]\u0003", - "^\u0003^\u0003_\u0003_\u0003`\u0003`\u0003`\u0003a\u0003a\u0003a\u0003", - "a\u0003b\u0003b\u0003b\u0003c\u0003c\u0003d\u0003d\u0003e\u0003e\u0003", - "e\u0003f\u0003f\u0003f\u0003g\u0003g\u0003g\u0003h\u0003h\u0003h\u0003", - "i\u0003i\u0003j\u0003j\u0003k\u0003k\u0003k\u0003l\u0003l\u0003l\u0003", - "l\u0003l\u0003m\u0003m\u0003m\u0003m\u0003n\u0003n\u0003n\u0003n\u0003", - "n\u0003o\u0003o\u0003o\u0003o\u0003p\u0003p\u0003p\u0003q\u0003q\u0003", - "q\u0003r\u0003r\u0003s\u0003s\u0004\u00f4\u0102\u0002t\u0003\u0003\u0005", - "\u0004\u0007\u0005\t\u0002\u000b\u0002\r\u0002\u000f\u0002\u0011\u0002", - "\u0013\u0002\u0015\u0002\u0017\u0002\u0019\u0002\u001b\u0002\u001d\u0002", - "\u001f\u0002!\u0002#\u0002%\u0002\'\u0002)\u0002+\u0002-\u0002/\u0002", - "1\u00023\u00025\u00027\u00029\u0002;\u0002=\u0002?\u0002A\u0002C\u0002", - "E\u0002G\u0002I\u0006K\u0007M\bO\tQ\nS\u000bU\fW\rY\u000e[\u000f]\u0010", - "_\u0011a\u0012c\u0013e\u0014g\u0015i\u0016k\u0017m\u0018o\u0019q\u001a", - "s\u001bu\u001cw\u001dy\u001e{\u001f} \u007f!\u0081\"\u0083#\u0085$\u0087", - "%\u0089&\u008b\'\u008d(\u008f)\u0091*\u0093+\u0095,\u0097-\u0099.\u009b", - "/\u009d0\u009f1\u00a12\u00a33\u00a54\u00a75\u00a96\u00ab7\u00ad8\u00af", - "9\u00b1:\u00b3;\u00b5<\u00b7=\u00b9>\u00bb?\u00bd@\u00bfA\u00c1B\u00c3", - "C\u00c5D\u00c7E\u00c9F\u00cbG\u00cdH\u00cfI\u00d1J\u00d3K\u00d5L\u00d7", - "M\u00d9N\u00dbO\u00ddP\u00dfQ\u00e1R\u00e3S\u00e5T\u0003\u0002&\u0005", - "\u0002\u000b\f\u000f\u000f\"\"\u0004\u0002CCcc\u0004\u0002DDdd\u0004", - "\u0002EEee\u0004\u0002FFff\u0004\u0002GGgg\u0004\u0002HHhh\u0004\u0002", - "IIii\u0004\u0002JJjj\u0004\u0002KKkk\u0004\u0002LLll\u0004\u0002MMm", - "m\u0004\u0002NNnn\u0004\u0002OOoo\u0004\u0002PPpp\u0004\u0002QQqq\u0004", - "\u0002RRrr\u0004\u0002SSss\u0004\u0002TTtt\u0004\u0002UUuu\u0004\u0002", - "VVvv\u0004\u0002WWww\u0004\u0002XXxx\u0004\u0002YYyy\u0004\u0002ZZz", - "z\u0004\u0002[[{{\u0004\u0002\\\\||\u0004\u00022;CH\u0003\u00022;\u0004", - "\u0002$$^^\u0004\u0002))^^\u0004\u0002^^bb\u0005\u0002C\\aac|\u0006", - "\u00022;C\\aac|\u0006\u0002C\\aac|\u00a3\u0001\u0007\u00022;C\\aac|", - "\u00a3\u0001\u0002\u0280\u0002\u0003\u0003\u0002\u0002\u0002\u0002\u0005", - "\u0003\u0002\u0002\u0002\u0002\u0007\u0003\u0002\u0002\u0002\u0002I", - "\u0003\u0002\u0002\u0002\u0002K\u0003\u0002\u0002\u0002\u0002M\u0003", - "\u0002\u0002\u0002\u0002O\u0003\u0002\u0002\u0002\u0002Q\u0003\u0002", - "\u0002\u0002\u0002S\u0003\u0002\u0002\u0002\u0002U\u0003\u0002\u0002", - "\u0002\u0002W\u0003\u0002\u0002\u0002\u0002Y\u0003\u0002\u0002\u0002", - "\u0002[\u0003\u0002\u0002\u0002\u0002]\u0003\u0002\u0002\u0002\u0002", - "_\u0003\u0002\u0002\u0002\u0002a\u0003\u0002\u0002\u0002\u0002c\u0003", - "\u0002\u0002\u0002\u0002e\u0003\u0002\u0002\u0002\u0002g\u0003\u0002", - "\u0002\u0002\u0002i\u0003\u0002\u0002\u0002\u0002k\u0003\u0002\u0002", - "\u0002\u0002m\u0003\u0002\u0002\u0002\u0002o\u0003\u0002\u0002\u0002", - "\u0002q\u0003\u0002\u0002\u0002\u0002s\u0003\u0002\u0002\u0002\u0002", - "u\u0003\u0002\u0002\u0002\u0002w\u0003\u0002\u0002\u0002\u0002y\u0003", - "\u0002\u0002\u0002\u0002{\u0003\u0002\u0002\u0002\u0002}\u0003\u0002", - "\u0002\u0002\u0002\u007f\u0003\u0002\u0002\u0002\u0002\u0081\u0003\u0002", - "\u0002\u0002\u0002\u0083\u0003\u0002\u0002\u0002\u0002\u0085\u0003\u0002", - "\u0002\u0002\u0002\u0087\u0003\u0002\u0002\u0002\u0002\u0089\u0003\u0002", - "\u0002\u0002\u0002\u008b\u0003\u0002\u0002\u0002\u0002\u008d\u0003\u0002", - "\u0002\u0002\u0002\u008f\u0003\u0002\u0002\u0002\u0002\u0091\u0003\u0002", - "\u0002\u0002\u0002\u0093\u0003\u0002\u0002\u0002\u0002\u0095\u0003\u0002", - "\u0002\u0002\u0002\u0097\u0003\u0002\u0002\u0002\u0002\u0099\u0003\u0002", - "\u0002\u0002\u0002\u009b\u0003\u0002\u0002\u0002\u0002\u009d\u0003\u0002", - "\u0002\u0002\u0002\u009f\u0003\u0002\u0002\u0002\u0002\u00a1\u0003\u0002", - "\u0002\u0002\u0002\u00a3\u0003\u0002\u0002\u0002\u0002\u00a5\u0003\u0002", - "\u0002\u0002\u0002\u00a7\u0003\u0002\u0002\u0002\u0002\u00a9\u0003\u0002", - "\u0002\u0002\u0002\u00ab\u0003\u0002\u0002\u0002\u0002\u00ad\u0003\u0002", - "\u0002\u0002\u0002\u00af\u0003\u0002\u0002\u0002\u0002\u00b1\u0003\u0002", - "\u0002\u0002\u0002\u00b3\u0003\u0002\u0002\u0002\u0002\u00b5\u0003\u0002", - "\u0002\u0002\u0002\u00b7\u0003\u0002\u0002\u0002\u0002\u00b9\u0003\u0002", - "\u0002\u0002\u0002\u00bb\u0003\u0002\u0002\u0002\u0002\u00bd\u0003\u0002", - "\u0002\u0002\u0002\u00bf\u0003\u0002\u0002\u0002\u0002\u00c1\u0003\u0002", - "\u0002\u0002\u0002\u00c3\u0003\u0002\u0002\u0002\u0002\u00c5\u0003\u0002", - "\u0002\u0002\u0002\u00c7\u0003\u0002\u0002\u0002\u0002\u00c9\u0003\u0002", - "\u0002\u0002\u0002\u00cb\u0003\u0002\u0002\u0002\u0002\u00cd\u0003\u0002", - "\u0002\u0002\u0002\u00cf\u0003\u0002\u0002\u0002\u0002\u00d1\u0003\u0002", - "\u0002\u0002\u0002\u00d3\u0003\u0002\u0002\u0002\u0002\u00d5\u0003\u0002", - "\u0002\u0002\u0002\u00d7\u0003\u0002\u0002\u0002\u0002\u00d9\u0003\u0002", - "\u0002\u0002\u0002\u00db\u0003\u0002\u0002\u0002\u0002\u00dd\u0003\u0002", - "\u0002\u0002\u0002\u00df\u0003\u0002\u0002\u0002\u0002\u00e1\u0003\u0002", - "\u0002\u0002\u0002\u00e3\u0003\u0002\u0002\u0002\u0002\u00e5\u0003\u0002", - "\u0002\u0002\u0003\u00e8\u0003\u0002\u0002\u0002\u0005\u00ee\u0003\u0002", - "\u0002\u0002\u0007\u00fc\u0003\u0002\u0002\u0002\t\u0109\u0003\u0002", - "\u0002\u0002\u000b\u010b\u0003\u0002\u0002\u0002\r\u010d\u0003\u0002", - "\u0002\u0002\u000f\u010f\u0003\u0002\u0002\u0002\u0011\u0111\u0003\u0002", - "\u0002\u0002\u0013\u0113\u0003\u0002\u0002\u0002\u0015\u0115\u0003\u0002", - "\u0002\u0002\u0017\u0117\u0003\u0002\u0002\u0002\u0019\u0119\u0003\u0002", - "\u0002\u0002\u001b\u011b\u0003\u0002\u0002\u0002\u001d\u011d\u0003\u0002", - "\u0002\u0002\u001f\u011f\u0003\u0002\u0002\u0002!\u0121\u0003\u0002", - "\u0002\u0002#\u0123\u0003\u0002\u0002\u0002%\u0125\u0003\u0002\u0002", - "\u0002\'\u0127\u0003\u0002\u0002\u0002)\u0129\u0003\u0002\u0002\u0002", - "+\u012b\u0003\u0002\u0002\u0002-\u012d\u0003\u0002\u0002\u0002/\u012f", - "\u0003\u0002\u0002\u00021\u0131\u0003\u0002\u0002\u00023\u0133\u0003", - "\u0002\u0002\u00025\u0135\u0003\u0002\u0002\u00027\u0137\u0003\u0002", - "\u0002\u00029\u0139\u0003\u0002\u0002\u0002;\u013b\u0003\u0002\u0002", - "\u0002=\u013d\u0003\u0002\u0002\u0002?\u013f\u0003\u0002\u0002\u0002", - "A\u0141\u0003\u0002\u0002\u0002C\u0143\u0003\u0002\u0002\u0002E\u014e", - "\u0003\u0002\u0002\u0002G\u0159\u0003\u0002\u0002\u0002I\u0166\u0003", - "\u0002\u0002\u0002K\u016b\u0003\u0002\u0002\u0002M\u0171\u0003\u0002", - "\u0002\u0002O\u0177\u0003\u0002\u0002\u0002Q\u0183\u0003\u0002\u0002", - "\u0002S\u0185\u0003\u0002\u0002\u0002U\u0187\u0003\u0002\u0002\u0002", - "W\u018a\u0003\u0002\u0002\u0002Y\u018c\u0003\u0002\u0002\u0002[\u018f", - "\u0003\u0002\u0002\u0002]\u0191\u0003\u0002\u0002\u0002_\u0193\u0003", - "\u0002\u0002\u0002a\u0195\u0003\u0002\u0002\u0002c\u0197\u0003\u0002", - "\u0002\u0002e\u0199\u0003\u0002\u0002\u0002g\u01a3\u0003\u0002\u0002", - "\u0002i\u01a7\u0003\u0002\u0002\u0002k\u01c3\u0003\u0002\u0002\u0002", - "m\u01d2\u0003\u0002\u0002\u0002o\u01d5\u0003\u0002\u0002\u0002q\u01d7", - "\u0003\u0002\u0002\u0002s\u01d9\u0003\u0002\u0002\u0002u\u01db\u0003", - "\u0002\u0002\u0002w\u01e2\u0003\u0002\u0002\u0002y\u01e9\u0003\u0002", - "\u0002\u0002{\u01eb\u0003\u0002\u0002\u0002}\u01ee\u0003\u0002\u0002", - "\u0002\u007f\u01f1\u0003\u0002\u0002\u0002\u0081\u01f4\u0003\u0002\u0002", - "\u0002\u0083\u01f7\u0003\u0002\u0002\u0002\u0085\u01f9\u0003\u0002\u0002", - "\u0002\u0087\u01fb\u0003\u0002\u0002\u0002\u0089\u01fe\u0003\u0002\u0002", - "\u0002\u008b\u0201\u0003\u0002\u0002\u0002\u008d\u0203\u0003\u0002\u0002", - "\u0002\u008f\u0205\u0003\u0002\u0002\u0002\u0091\u0208\u0003\u0002\u0002", - "\u0002\u0093\u020a\u0003\u0002\u0002\u0002\u0095\u020d\u0003\u0002\u0002", - "\u0002\u0097\u0210\u0003\u0002\u0002\u0002\u0099\u0212\u0003\u0002\u0002", - "\u0002\u009b\u0215\u0003\u0002\u0002\u0002\u009d\u0218\u0003\u0002\u0002", - "\u0002\u009f\u021c\u0003\u0002\u0002\u0002\u00a1\u021f\u0003\u0002\u0002", - "\u0002\u00a3\u0222\u0003\u0002\u0002\u0002\u00a5\u0226\u0003\u0002\u0002", - "\u0002\u00a7\u022a\u0003\u0002\u0002\u0002\u00a9\u022c\u0003\u0002\u0002", - "\u0002\u00ab\u022f\u0003\u0002\u0002\u0002\u00ad\u0232\u0003\u0002\u0002", - "\u0002\u00af\u0235\u0003\u0002\u0002\u0002\u00b1\u0238\u0003\u0002\u0002", - "\u0002\u00b3\u023c\u0003\u0002\u0002\u0002\u00b5\u023f\u0003\u0002\u0002", - "\u0002\u00b7\u0243\u0003\u0002\u0002\u0002\u00b9\u0247\u0003\u0002\u0002", - "\u0002\u00bb\u0249\u0003\u0002\u0002\u0002\u00bd\u024b\u0003\u0002\u0002", - "\u0002\u00bf\u024d\u0003\u0002\u0002\u0002\u00c1\u0250\u0003\u0002\u0002", - "\u0002\u00c3\u0254\u0003\u0002\u0002\u0002\u00c5\u0257\u0003\u0002\u0002", - "\u0002\u00c7\u0259\u0003\u0002\u0002\u0002\u00c9\u025b\u0003\u0002\u0002", - "\u0002\u00cb\u025e\u0003\u0002\u0002\u0002\u00cd\u0261\u0003\u0002\u0002", - "\u0002\u00cf\u0264\u0003\u0002\u0002\u0002\u00d1\u0267\u0003\u0002\u0002", - "\u0002\u00d3\u0269\u0003\u0002\u0002\u0002\u00d5\u026b\u0003\u0002\u0002", - "\u0002\u00d7\u026e\u0003\u0002\u0002\u0002\u00d9\u0273\u0003\u0002\u0002", - "\u0002\u00db\u0277\u0003\u0002\u0002\u0002\u00dd\u027c\u0003\u0002\u0002", - "\u0002\u00df\u0280\u0003\u0002\u0002\u0002\u00e1\u0283\u0003\u0002\u0002", - "\u0002\u00e3\u0286\u0003\u0002\u0002\u0002\u00e5\u0288\u0003\u0002\u0002", - "\u0002\u00e7\u00e9\t\u0002\u0002\u0002\u00e8\u00e7\u0003\u0002\u0002", - "\u0002\u00e9\u00ea\u0003\u0002\u0002\u0002\u00ea\u00e8\u0003\u0002\u0002", - "\u0002\u00ea\u00eb\u0003\u0002\u0002\u0002\u00eb\u00ec\u0003\u0002\u0002", - "\u0002\u00ec\u00ed\b\u0002\u0002\u0002\u00ed\u0004\u0003\u0002\u0002", - "\u0002\u00ee\u00ef\u00071\u0002\u0002\u00ef\u00f0\u0007,\u0002\u0002", - "\u00f0\u00f4\u0003\u0002\u0002\u0002\u00f1\u00f3\u000b\u0002\u0002\u0002", - "\u00f2\u00f1\u0003\u0002\u0002\u0002\u00f3\u00f6\u0003\u0002\u0002\u0002", - "\u00f4\u00f5\u0003\u0002\u0002\u0002\u00f4\u00f2\u0003\u0002\u0002\u0002", - "\u00f5\u00f7\u0003\u0002\u0002\u0002\u00f6\u00f4\u0003\u0002\u0002\u0002", - "\u00f7\u00f8\u0007,\u0002\u0002\u00f8\u00f9\u00071\u0002\u0002\u00f9", - "\u00fa\u0003\u0002\u0002\u0002\u00fa\u00fb\b\u0003\u0002\u0002\u00fb", - "\u0006\u0003\u0002\u0002\u0002\u00fc\u00fd\u00071\u0002\u0002\u00fd", - "\u00fe\u00071\u0002\u0002\u00fe\u0102\u0003\u0002\u0002\u0002\u00ff", - "\u0101\u000b\u0002\u0002\u0002\u0100\u00ff\u0003\u0002\u0002\u0002\u0101", - "\u0104\u0003\u0002\u0002\u0002\u0102\u0103\u0003\u0002\u0002\u0002\u0102", - "\u0100\u0003\u0002\u0002\u0002\u0103\u0105\u0003\u0002\u0002\u0002\u0104", - "\u0102\u0003\u0002\u0002\u0002\u0105\u0106\u0007\f\u0002\u0002\u0106", - "\u0107\u0003\u0002\u0002\u0002\u0107\u0108\b\u0004\u0002\u0002\u0108", - "\b\u0003\u0002\u0002\u0002\u0109\u010a\t\u0003\u0002\u0002\u010a\n\u0003", - "\u0002\u0002\u0002\u010b\u010c\t\u0004\u0002\u0002\u010c\f\u0003\u0002", - "\u0002\u0002\u010d\u010e\t\u0005\u0002\u0002\u010e\u000e\u0003\u0002", - "\u0002\u0002\u010f\u0110\t\u0006\u0002\u0002\u0110\u0010\u0003\u0002", - "\u0002\u0002\u0111\u0112\t\u0007\u0002\u0002\u0112\u0012\u0003\u0002", - "\u0002\u0002\u0113\u0114\t\b\u0002\u0002\u0114\u0014\u0003\u0002\u0002", - "\u0002\u0115\u0116\t\t\u0002\u0002\u0116\u0016\u0003\u0002\u0002\u0002", - "\u0117\u0118\t\n\u0002\u0002\u0118\u0018\u0003\u0002\u0002\u0002\u0119", - "\u011a\t\u000b\u0002\u0002\u011a\u001a\u0003\u0002\u0002\u0002\u011b", - "\u011c\t\f\u0002\u0002\u011c\u001c\u0003\u0002\u0002\u0002\u011d\u011e", - "\t\r\u0002\u0002\u011e\u001e\u0003\u0002\u0002\u0002\u011f\u0120\t\u000e", - "\u0002\u0002\u0120 \u0003\u0002\u0002\u0002\u0121\u0122\t\u000f\u0002", - "\u0002\u0122\"\u0003\u0002\u0002\u0002\u0123\u0124\t\u0010\u0002\u0002", - "\u0124$\u0003\u0002\u0002\u0002\u0125\u0126\t\u0011\u0002\u0002\u0126", - "&\u0003\u0002\u0002\u0002\u0127\u0128\t\u0012\u0002\u0002\u0128(\u0003", - "\u0002\u0002\u0002\u0129\u012a\t\u0013\u0002\u0002\u012a*\u0003\u0002", - "\u0002\u0002\u012b\u012c\t\u0014\u0002\u0002\u012c,\u0003\u0002\u0002", - "\u0002\u012d\u012e\t\u0015\u0002\u0002\u012e.\u0003\u0002\u0002\u0002", - "\u012f\u0130\t\u0016\u0002\u0002\u01300\u0003\u0002\u0002\u0002\u0131", - "\u0132\t\u0017\u0002\u0002\u01322\u0003\u0002\u0002\u0002\u0133\u0134", - "\t\u0018\u0002\u0002\u01344\u0003\u0002\u0002\u0002\u0135\u0136\t\u0019", - "\u0002\u0002\u01366\u0003\u0002\u0002\u0002\u0137\u0138\t\u001a\u0002", - "\u0002\u01388\u0003\u0002\u0002\u0002\u0139\u013a\t\u001b\u0002\u0002", - "\u013a:\u0003\u0002\u0002\u0002\u013b\u013c\t\u001c\u0002\u0002\u013c", - "<\u0003\u0002\u0002\u0002\u013d\u013e\u0007a\u0002\u0002\u013e>\u0003", - "\u0002\u0002\u0002\u013f\u0140\t\u001d\u0002\u0002\u0140@\u0003\u0002", - "\u0002\u0002\u0141\u0142\t\u001e\u0002\u0002\u0142B\u0003\u0002\u0002", - "\u0002\u0143\u0149\u0007$\u0002\u0002\u0144\u0145\u0007^\u0002\u0002", - "\u0145\u0148\u000b\u0002\u0002\u0002\u0146\u0148\n\u001f\u0002\u0002", - "\u0147\u0144\u0003\u0002\u0002\u0002\u0147\u0146\u0003\u0002\u0002\u0002", - "\u0148\u014b\u0003\u0002\u0002\u0002\u0149\u0147\u0003\u0002\u0002\u0002", - "\u0149\u014a\u0003\u0002\u0002\u0002\u014a\u014c\u0003\u0002\u0002\u0002", - "\u014b\u0149\u0003\u0002\u0002\u0002\u014c\u014d\u0007$\u0002\u0002", - "\u014dD\u0003\u0002\u0002\u0002\u014e\u0154\u0007)\u0002\u0002\u014f", - "\u0150\u0007^\u0002\u0002\u0150\u0153\u000b\u0002\u0002\u0002\u0151", - "\u0153\n \u0002\u0002\u0152\u014f\u0003\u0002\u0002\u0002\u0152\u0151", - "\u0003\u0002\u0002\u0002\u0153\u0156\u0003\u0002\u0002\u0002\u0154\u0152", - "\u0003\u0002\u0002\u0002\u0154\u0155\u0003\u0002\u0002\u0002\u0155\u0157", - "\u0003\u0002\u0002\u0002\u0156\u0154\u0003\u0002\u0002\u0002\u0157\u0158", - "\u0007)\u0002\u0002\u0158F\u0003\u0002\u0002\u0002\u0159\u0161\u0007", - "b\u0002\u0002\u015a\u015b\u0007^\u0002\u0002\u015b\u0160\u000b\u0002", - "\u0002\u0002\u015c\u015d\u0007b\u0002\u0002\u015d\u0160\u0007b\u0002", - "\u0002\u015e\u0160\n!\u0002\u0002\u015f\u015a\u0003\u0002\u0002\u0002", - "\u015f\u015c\u0003\u0002\u0002\u0002\u015f\u015e\u0003\u0002\u0002\u0002", - "\u0160\u0163\u0003\u0002\u0002\u0002\u0161\u015f\u0003\u0002\u0002\u0002", - "\u0161\u0162\u0003\u0002\u0002\u0002\u0162\u0164\u0003\u0002\u0002\u0002", - "\u0163\u0161\u0003\u0002\u0002\u0002\u0164\u0165\u0007b\u0002\u0002", - "\u0165H\u0003\u0002\u0002\u0002\u0166\u0167\u0005/\u0018\u0002\u0167", - "\u0168\u0005+\u0016\u0002\u0168\u0169\u00051\u0019\u0002\u0169\u016a", - "\u0005\u0011\t\u0002\u016aJ\u0003\u0002\u0002\u0002\u016b\u016c\u0005", - "\u0013\n\u0002\u016c\u016d\u0005\t\u0005\u0002\u016d\u016e\u0005\u001f", - "\u0010\u0002\u016e\u016f\u0005-\u0017\u0002\u016f\u0170\u0005\u0011", - "\t\u0002\u0170L\u0003\u0002\u0002\u0002\u0171\u0172\u0005\u0013\n\u0002", - "\u0172\u0173\u0005\u0019\r\u0002\u0173\u0174\u0005\u0011\t\u0002\u0174", - "\u0175\u0005\u001f\u0010\u0002\u0175\u0176\u0005\u000f\b\u0002\u0176", - "N\u0003\u0002\u0002\u0002\u0177\u0178\u0005\u0013\n\u0002\u0178\u0179", - "\u0005\u0019\r\u0002\u0179\u017a\u0005\u0011\t\u0002\u017a\u017b\u0005", - "\u001f\u0010\u0002\u017b\u017c\u0005\u000f\b\u0002\u017c\u017d\u0005", - "=\u001f\u0002\u017d\u017e\u0005\u000b\u0006\u0002\u017e\u017f\u0005", - "9\u001d\u0002\u017f\u0180\u0005=\u001f\u0002\u0180\u0181\u0005\u0019", - "\r\u0002\u0181\u0182\u0005\u000f\b\u0002\u0182P\u0003\u0002\u0002\u0002", - "\u0183\u0184\u0007.\u0002\u0002\u0184R\u0003\u0002\u0002\u0002\u0185", - "\u0186\u0007<\u0002\u0002\u0186T\u0003\u0002\u0002\u0002\u0187\u0188", - "\u0007<\u0002\u0002\u0188\u0189\u0007<\u0002\u0002\u0189V\u0003\u0002", - "\u0002\u0002\u018a\u018b\u0007&\u0002\u0002\u018bX\u0003\u0002\u0002", - "\u0002\u018c\u018d\u0007&\u0002\u0002\u018d\u018e\u0007&\u0002\u0002", - "\u018eZ\u0003\u0002\u0002\u0002\u018f\u0190\u0007,\u0002\u0002\u0190", - "\\\u0003\u0002\u0002\u0002\u0191\u0192\u0007*\u0002\u0002\u0192^\u0003", - "\u0002\u0002\u0002\u0193\u0194\u0007+\u0002\u0002\u0194`\u0003\u0002", - "\u0002\u0002\u0195\u0196\u0007]\u0002\u0002\u0196b\u0003\u0002\u0002", - "\u0002\u0197\u0198\u0007_\u0002\u0002\u0198d\u0003\u0002\u0002\u0002", - "\u0199\u019a\u0005\u000b\u0006\u0002\u019a\u019e\u0007)\u0002\u0002", - "\u019b\u019d\u000423\u0002\u019c\u019b\u0003\u0002\u0002\u0002\u019d", - "\u01a0\u0003\u0002\u0002\u0002\u019e\u019c\u0003\u0002\u0002\u0002\u019e", - "\u019f\u0003\u0002\u0002\u0002\u019f\u01a1\u0003\u0002\u0002\u0002\u01a0", - "\u019e\u0003\u0002\u0002\u0002\u01a1\u01a2\u0007)\u0002\u0002\u01a2", - "f\u0003\u0002\u0002\u0002\u01a3\u01a4\u0005\u0011\t\u0002\u01a4\u01a5", - "\u0005E#\u0002\u01a5h\u0003\u0002\u0002\u0002\u01a6\u01a8\u0007/\u0002", - "\u0002\u01a7\u01a6\u0003\u0002\u0002\u0002\u01a7\u01a8\u0003\u0002\u0002", - "\u0002\u01a8\u01aa\u0003\u0002\u0002\u0002\u01a9\u01ab\u0005A!\u0002", - "\u01aa\u01a9\u0003\u0002\u0002\u0002\u01ab\u01ac\u0003\u0002\u0002\u0002", - "\u01ac\u01aa\u0003\u0002\u0002\u0002\u01ac\u01ad\u0003\u0002\u0002\u0002", - "\u01ad\u01ae\u0003\u0002\u0002\u0002\u01ae\u01b0\u00070\u0002\u0002", - "\u01af\u01b1\u0005A!\u0002\u01b0\u01af\u0003\u0002\u0002\u0002\u01b1", - "\u01b2\u0003\u0002\u0002\u0002\u01b2\u01b0\u0003\u0002\u0002\u0002\u01b2", - "\u01b3\u0003\u0002\u0002\u0002\u01b3\u01c0\u0003\u0002\u0002\u0002\u01b4", - "\u01b8\u0005\u0011\t\u0002\u01b5\u01b7\u0007/\u0002\u0002\u01b6\u01b5", - "\u0003\u0002\u0002\u0002\u01b7\u01ba\u0003\u0002\u0002\u0002\u01b8\u01b6", - "\u0003\u0002\u0002\u0002\u01b8\u01b9\u0003\u0002\u0002\u0002\u01b9\u01bc", - "\u0003\u0002\u0002\u0002\u01ba\u01b8\u0003\u0002\u0002\u0002\u01bb\u01bd", - "\u0005A!\u0002\u01bc\u01bb\u0003\u0002\u0002\u0002\u01bd\u01be\u0003", - "\u0002\u0002\u0002\u01be\u01bc\u0003\u0002\u0002\u0002\u01be\u01bf\u0003", - "\u0002\u0002\u0002\u01bf\u01c1\u0003\u0002\u0002\u0002\u01c0\u01b4\u0003", - "\u0002\u0002\u0002\u01c0\u01c1\u0003\u0002\u0002\u0002\u01c1j\u0003", - "\u0002\u0002\u0002\u01c2\u01c4\u0007/\u0002\u0002\u01c3\u01c2\u0003", - "\u0002\u0002\u0002\u01c3\u01c4\u0003\u0002\u0002\u0002\u01c4\u01c6\u0003", - "\u0002\u0002\u0002\u01c5\u01c7\u0005A!\u0002\u01c6\u01c5\u0003\u0002", - "\u0002\u0002\u01c7\u01c8\u0003\u0002\u0002\u0002\u01c8\u01c6\u0003\u0002", - "\u0002\u0002\u01c8\u01c9\u0003\u0002\u0002\u0002\u01c9\u01d0\u0003\u0002", - "\u0002\u0002\u01ca\u01cc\u0005\u0011\t\u0002\u01cb\u01cd\u0005A!\u0002", - "\u01cc\u01cb\u0003\u0002\u0002\u0002\u01cd\u01ce\u0003\u0002\u0002\u0002", - "\u01ce\u01cc\u0003\u0002\u0002\u0002\u01ce\u01cf\u0003\u0002\u0002\u0002", - "\u01cf\u01d1\u0003\u0002\u0002\u0002\u01d0\u01ca\u0003\u0002\u0002\u0002", - "\u01d0\u01d1\u0003\u0002\u0002\u0002\u01d1l\u0003\u0002\u0002\u0002", - "\u01d2\u01d3\u0007z\u0002\u0002\u01d3\u01d4\u0005E#\u0002\u01d4n\u0003", - "\u0002\u0002\u0002\u01d5\u01d6\u00070\u0002\u0002\u01d6p\u0003\u0002", - "\u0002\u0002\u01d7\u01d8\u0005E#\u0002\u01d8r\u0003\u0002\u0002\u0002", - "\u01d9\u01da\u0005C\"\u0002\u01dat\u0003\u0002\u0002\u0002\u01db\u01df", - "\t\"\u0002\u0002\u01dc\u01de\t#\u0002\u0002\u01dd\u01dc\u0003\u0002", - "\u0002\u0002\u01de\u01e1\u0003\u0002\u0002\u0002\u01df\u01dd\u0003\u0002", - "\u0002\u0002\u01df\u01e0\u0003\u0002\u0002\u0002\u01e0v\u0003\u0002", - "\u0002\u0002\u01e1\u01df\u0003\u0002\u0002\u0002\u01e2\u01e6\t$\u0002", - "\u0002\u01e3\u01e5\t%\u0002\u0002\u01e4\u01e3\u0003\u0002\u0002\u0002", - "\u01e5\u01e8\u0003\u0002\u0002\u0002\u01e6\u01e4\u0003\u0002\u0002\u0002", - "\u01e6\u01e7\u0003\u0002\u0002\u0002\u01e7x\u0003\u0002\u0002\u0002", - "\u01e8\u01e6\u0003\u0002\u0002\u0002\u01e9\u01ea\u0007(\u0002\u0002", - "\u01eaz\u0003\u0002\u0002\u0002\u01eb\u01ec\u0007(\u0002\u0002\u01ec", - "\u01ed\u0007(\u0002\u0002\u01ed|\u0003\u0002\u0002\u0002\u01ee\u01ef", - "\u0007(\u0002\u0002\u01ef\u01f0\u0007>\u0002\u0002\u01f0~\u0003\u0002", - "\u0002\u0002\u01f1\u01f2\u0007B\u0002\u0002\u01f2\u01f3\u0007B\u0002", - "\u0002\u01f3\u0080\u0003\u0002\u0002\u0002\u01f4\u01f5\u0007B\u0002", - "\u0002\u01f5\u01f6\u0007@\u0002\u0002\u01f6\u0082\u0003\u0002\u0002", - "\u0002\u01f7\u01f8\u0007B\u0002\u0002\u01f8\u0084\u0003\u0002\u0002", - "\u0002\u01f9\u01fa\u0007#\u0002\u0002\u01fa\u0086\u0003\u0002\u0002", - "\u0002\u01fb\u01fc\u0007#\u0002\u0002\u01fc\u01fd\u0007#\u0002\u0002", - "\u01fd\u0088\u0003\u0002\u0002\u0002\u01fe\u01ff\u0007#\u0002\u0002", - "\u01ff\u0200\u0007?\u0002\u0002\u0200\u008a\u0003\u0002\u0002\u0002", - "\u0201\u0202\u0007`\u0002\u0002\u0202\u008c\u0003\u0002\u0002\u0002", - "\u0203\u0204\u0007?\u0002\u0002\u0204\u008e\u0003\u0002\u0002\u0002", - "\u0205\u0206\u0007?\u0002\u0002\u0206\u0207\u0007@\u0002\u0002\u0207", - "\u0090\u0003\u0002\u0002\u0002\u0208\u0209\u0007@\u0002\u0002\u0209", - "\u0092\u0003\u0002\u0002\u0002\u020a\u020b\u0007@\u0002\u0002\u020b", - "\u020c\u0007?\u0002\u0002\u020c\u0094\u0003\u0002\u0002\u0002\u020d", - "\u020e\u0007@\u0002\u0002\u020e\u020f\u0007@\u0002\u0002\u020f\u0096", - "\u0003\u0002\u0002\u0002\u0210\u0211\u0007%\u0002\u0002\u0211\u0098", - "\u0003\u0002\u0002\u0002\u0212\u0213\u0007%\u0002\u0002\u0213\u0214", - "\u0007?\u0002\u0002\u0214\u009a\u0003\u0002\u0002\u0002\u0215\u0216", - "\u0007%\u0002\u0002\u0216\u0217\u0007@\u0002\u0002\u0217\u009c\u0003", - "\u0002\u0002\u0002\u0218\u0219\u0007%\u0002\u0002\u0219\u021a\u0007", - "@\u0002\u0002\u021a\u021b\u0007@\u0002\u0002\u021b\u009e\u0003\u0002", - "\u0002\u0002\u021c\u021d\u0007%\u0002\u0002\u021d\u021e\u0007%\u0002", - "\u0002\u021e\u00a0\u0003\u0002\u0002\u0002\u021f\u0220\u0007/\u0002", - "\u0002\u0220\u0221\u0007@\u0002\u0002\u0221\u00a2\u0003\u0002\u0002", - "\u0002\u0222\u0223\u0007/\u0002\u0002\u0223\u0224\u0007@\u0002\u0002", - "\u0224\u0225\u0007@\u0002\u0002\u0225\u00a4\u0003\u0002\u0002\u0002", - "\u0226\u0227\u0007/\u0002\u0002\u0227\u0228\u0007~\u0002\u0002\u0228", - "\u0229\u0007/\u0002\u0002\u0229\u00a6\u0003\u0002\u0002\u0002\u022a", - "\u022b\u0007>\u0002\u0002\u022b\u00a8\u0003\u0002\u0002\u0002\u022c", - "\u022d\u0007>\u0002\u0002\u022d\u022e\u0007?\u0002\u0002\u022e\u00aa", - "\u0003\u0002\u0002\u0002\u022f\u0230\u0007>\u0002\u0002\u0230\u0231", - "\u0007B\u0002\u0002\u0231\u00ac\u0003\u0002\u0002\u0002\u0232\u0233", - "\u0007>\u0002\u0002\u0233\u0234\u0007`\u0002\u0002\u0234\u00ae\u0003", - "\u0002\u0002\u0002\u0235\u0236\u0007>\u0002\u0002\u0236\u0237\u0007", - "@\u0002\u0002\u0237\u00b0\u0003\u0002\u0002\u0002\u0238\u0239\u0007", - ">\u0002\u0002\u0239\u023a\u0007/\u0002\u0002\u023a\u023b\u0007@\u0002", - "\u0002\u023b\u00b2\u0003\u0002\u0002\u0002\u023c\u023d\u0007>\u0002", - "\u0002\u023d\u023e\u0007>\u0002\u0002\u023e\u00b4\u0003\u0002\u0002", - "\u0002\u023f\u0240\u0007>\u0002\u0002\u0240\u0241\u0007>\u0002\u0002", - "\u0241\u0242\u0007?\u0002\u0002\u0242\u00b6\u0003\u0002\u0002\u0002", - "\u0243\u0244\u0007>\u0002\u0002\u0244\u0245\u0007A\u0002\u0002\u0245", - "\u0246\u0007@\u0002\u0002\u0246\u00b8\u0003\u0002\u0002\u0002\u0247", - "\u0248\u0007/\u0002\u0002\u0248\u00ba\u0003\u0002\u0002\u0002\u0249", - "\u024a\u0007\'\u0002\u0002\u024a\u00bc\u0003\u0002\u0002\u0002\u024b", - "\u024c\u0007~\u0002\u0002\u024c\u00be\u0003\u0002\u0002\u0002\u024d", - "\u024e\u0007~\u0002\u0002\u024e\u024f\u0007~\u0002\u0002\u024f\u00c0", - "\u0003\u0002\u0002\u0002\u0250\u0251\u0007~\u0002\u0002\u0251\u0252", - "\u0007~\u0002\u0002\u0252\u0253\u00071\u0002\u0002\u0253\u00c2\u0003", - "\u0002\u0002\u0002\u0254\u0255\u0007~\u0002\u0002\u0255\u0256\u0007", - "1\u0002\u0002\u0256\u00c4\u0003\u0002\u0002\u0002\u0257\u0258\u0007", - "-\u0002\u0002\u0258\u00c6\u0003\u0002\u0002\u0002\u0259\u025a\u0007", - "A\u0002\u0002\u025a\u00c8\u0003\u0002\u0002\u0002\u025b\u025c\u0007", - "A\u0002\u0002\u025c\u025d\u0007(\u0002\u0002\u025d\u00ca\u0003\u0002", - "\u0002\u0002\u025e\u025f\u0007A\u0002\u0002\u025f\u0260\u0007%\u0002", - "\u0002\u0260\u00cc\u0003\u0002\u0002\u0002\u0261\u0262\u0007A\u0002", - "\u0002\u0262\u0263\u0007/\u0002\u0002\u0263\u00ce\u0003\u0002\u0002", - "\u0002\u0264\u0265\u0007A\u0002\u0002\u0265\u0266\u0007~\u0002\u0002", - "\u0266\u00d0\u0003\u0002\u0002\u0002\u0267\u0268\u00071\u0002\u0002", - "\u0268\u00d2\u0003\u0002\u0002\u0002\u0269\u026a\u0007\u0080\u0002\u0002", - "\u026a\u00d4\u0003\u0002\u0002\u0002\u026b\u026c\u0007\u0080\u0002\u0002", - "\u026c\u026d\u0007?\u0002\u0002\u026d\u00d6\u0003\u0002\u0002\u0002", - "\u026e\u026f\u0007\u0080\u0002\u0002\u026f\u0270\u0007@\u0002\u0002", - "\u0270\u0271\u0007?\u0002\u0002\u0271\u0272\u0007\u0080\u0002\u0002", - "\u0272\u00d8\u0003\u0002\u0002\u0002\u0273\u0274\u0007\u0080\u0002\u0002", - "\u0274\u0275\u0007@\u0002\u0002\u0275\u0276\u0007\u0080\u0002\u0002", - "\u0276\u00da\u0003\u0002\u0002\u0002\u0277\u0278\u0007\u0080\u0002\u0002", - "\u0278\u0279\u0007>\u0002\u0002\u0279\u027a\u0007?\u0002\u0002\u027a", - "\u027b\u0007\u0080\u0002\u0002\u027b\u00dc\u0003\u0002\u0002\u0002\u027c", - "\u027d\u0007\u0080\u0002\u0002\u027d\u027e\u0007>\u0002\u0002\u027e", - "\u027f\u0007\u0080\u0002\u0002\u027f\u00de\u0003\u0002\u0002\u0002\u0280", - "\u0281\u0007\u0080\u0002\u0002\u0281\u0282\u0007,\u0002\u0002\u0282", - "\u00e0\u0003\u0002\u0002\u0002\u0283\u0284\u0007\u0080\u0002\u0002\u0284", - "\u0285\u0007\u0080\u0002\u0002\u0285\u00e2\u0003\u0002\u0002\u0002\u0286", - "\u0287\u0007=\u0002\u0002\u0287\u00e4\u0003\u0002\u0002\u0002\u0288", - "\u0289\u000b\u0002\u0002\u0002\u0289\u00e6\u0003\u0002\u0002\u0002\u0019", - "\u0002\u00ea\u00f4\u0102\u0147\u0149\u0152\u0154\u015f\u0161\u019e\u01a7", - "\u01ac\u01b2\u01b8\u01be\u01c0\u01c3\u01c8\u01ce\u01d0\u01df\u01e6\u0003", - "\u0002\u0003\u0002"].join(""); + "p\tp\u0004q\tq\u0004r\tr\u0004s\ts\u0003\u0002\u0003\u0002\u0003\u0003", + "\u0003\u0003\u0003\u0004\u0003\u0004\u0003\u0005\u0003\u0005\u0003\u0006", + "\u0003\u0006\u0003\u0007\u0003\u0007\u0003\b\u0003\b\u0003\t\u0003\t", + "\u0003\n\u0003\n\u0003\u000b\u0003\u000b\u0003\f\u0003\f\u0003\r\u0003", + "\r\u0003\u000e\u0003\u000e\u0003\u000f\u0003\u000f\u0003\u0010\u0003", + "\u0010\u0003\u0011\u0003\u0011\u0003\u0012\u0003\u0012\u0003\u0013\u0003", + "\u0013\u0003\u0014\u0003\u0014\u0003\u0015\u0003\u0015\u0003\u0016\u0003", + "\u0016\u0003\u0017\u0003\u0017\u0003\u0018\u0003\u0018\u0003\u0019\u0003", + "\u0019\u0003\u001a\u0003\u001a\u0003\u001b\u0003\u001b\u0003\u001c\u0003", + "\u001c\u0003\u001d\u0003\u001d\u0003\u001e\u0003\u001e\u0003\u001f\u0003", + "\u001f\u0003\u001f\u0003\u001f\u0007\u001f\u0126\n\u001f\f\u001f\u000e", + "\u001f\u0129\u000b\u001f\u0003\u001f\u0003\u001f\u0003 \u0003 \u0003", + " \u0003 \u0007 \u0131\n \f \u000e \u0134\u000b \u0003 \u0003 \u0003", + "!\u0003!\u0003!\u0003!\u0003!\u0003!\u0007!\u013e\n!\f!\u000e!\u0141", + "\u000b!\u0003!\u0003!\u0003\"\u0003\"\u0003\"\u0003\"\u0007\"\u0149", + "\n\"\f\"\u000e\"\u014c\u000b\"\u0003\"\u0003\"\u0003\"\u0003#\u0003", + "#\u0003#\u0003#\u0007#\u0155\n#\f#\u000e#\u0158\u000b#\u0003$\u0006", + "$\u015b\n$\r$\u000e$\u015c\u0003%\u0003%\u0003%\u0003%\u0003%\u0003", + "&\u0003&\u0003&\u0003&\u0003&\u0003&\u0003\'\u0003\'\u0003\'\u0003\'", + "\u0003\'\u0003\'\u0003(\u0003(\u0003(\u0003(\u0003(\u0003(\u0003(\u0003", + "(\u0003(\u0003(\u0003(\u0003(\u0003)\u0003)\u0003*\u0003*\u0003+\u0003", + "+\u0003+\u0003,\u0003,\u0003-\u0003-\u0003-\u0003.\u0003.\u0003/\u0003", + "/\u00030\u00030\u00031\u00031\u00032\u00032\u00033\u00033\u00033\u0007", + "3\u0195\n3\f3\u000e3\u0198\u000b3\u00033\u00033\u00034\u00034\u0003", + "4\u00035\u00055\u01a0\n5\u00035\u00065\u01a3\n5\r5\u000e5\u01a4\u0003", + "5\u00035\u00065\u01a9\n5\r5\u000e5\u01aa\u00035\u00035\u00075\u01af", + "\n5\f5\u000e5\u01b2\u000b5\u00035\u00065\u01b5\n5\r5\u000e5\u01b6\u0005", + "5\u01b9\n5\u00036\u00056\u01bc\n6\u00036\u00066\u01bf\n6\r6\u000e6\u01c0", + "\u00036\u00036\u00066\u01c5\n6\r6\u000e6\u01c6\u00056\u01c9\n6\u0003", + "7\u00037\u00037\u00038\u00038\u00039\u00039\u0003:\u0003:\u0003;\u0003", + ";\u0007;\u01d6\n;\f;\u000e;\u01d9\u000b;\u0003<\u0003<\u0007<\u01dd", + "\n<\f<\u000e<\u01e0\u000b<\u0003=\u0003=\u0003>\u0003>\u0003>\u0003", + "?\u0003?\u0003?\u0003@\u0003@\u0003@\u0003A\u0003A\u0003A\u0003B\u0003", + "B\u0003C\u0003C\u0003D\u0003D\u0003D\u0003E\u0003E\u0003E\u0003F\u0003", + "F\u0003G\u0003G\u0003H\u0003H\u0003H\u0003I\u0003I\u0003J\u0003J\u0003", + "J\u0003K\u0003K\u0003K\u0003L\u0003L\u0003M\u0003M\u0003M\u0003N\u0003", + "N\u0003N\u0003O\u0003O\u0003O\u0003O\u0003P\u0003P\u0003P\u0003Q\u0003", + "Q\u0003Q\u0003R\u0003R\u0003R\u0003R\u0003S\u0003S\u0003S\u0003S\u0003", + "T\u0003T\u0003U\u0003U\u0003U\u0003V\u0003V\u0003V\u0003W\u0003W\u0003", + "W\u0003X\u0003X\u0003X\u0003Y\u0003Y\u0003Y\u0003Y\u0003Z\u0003Z\u0003", + "Z\u0003[\u0003[\u0003[\u0003[\u0003\\\u0003\\\u0003\\\u0003\\\u0003", + "]\u0003]\u0003^\u0003^\u0003_\u0003_\u0003`\u0003`\u0003`\u0003a\u0003", + "a\u0003a\u0003a\u0003b\u0003b\u0003b\u0003c\u0003c\u0003d\u0003d\u0003", + "e\u0003e\u0003e\u0003f\u0003f\u0003f\u0003g\u0003g\u0003g\u0003h\u0003", + "h\u0003h\u0003i\u0003i\u0003j\u0003j\u0003k\u0003k\u0003k\u0003l\u0003", + "l\u0003l\u0003l\u0003l\u0003m\u0003m\u0003m\u0003m\u0003n\u0003n\u0003", + "n\u0003n\u0003n\u0003o\u0003o\u0003o\u0003o\u0003p\u0003p\u0003p\u0003", + "q\u0003q\u0003q\u0003r\u0003r\u0003s\u0003s\u0002\u0002t\u0003\u0002", + "\u0005\u0002\u0007\u0002\t\u0002\u000b\u0002\r\u0002\u000f\u0002\u0011", + "\u0002\u0013\u0002\u0015\u0002\u0017\u0002\u0019\u0002\u001b\u0002\u001d", + "\u0002\u001f\u0002!\u0002#\u0002%\u0002\'\u0002)\u0002+\u0002-\u0002", + "/\u00021\u00023\u00025\u00027\u00029\u0002;\u0002=\u0002?\u0002A\u0002", + "C\u0003E\u0004G\u0005I\u0006K\u0007M\bO\tQ\nS\u000bU\fW\rY\u000e[\u000f", + "]\u0010_\u0011a\u0012c\u0013e\u0014g\u0015i\u0016k\u0017m\u0018o\u0019", + "q\u001as\u001bu\u001cw\u001dy\u001e{\u001f} \u007f!\u0081\"\u0083#\u0085", + "$\u0087%\u0089&\u008b\'\u008d(\u008f)\u0091*\u0093+\u0095,\u0097-\u0099", + ".\u009b/\u009d0\u009f1\u00a12\u00a33\u00a54\u00a75\u00a96\u00ab7\u00ad", + "8\u00af9\u00b1:\u00b3;\u00b5<\u00b7=\u00b9>\u00bb?\u00bd@\u00bfA\u00c1", + "B\u00c3C\u00c5D\u00c7E\u00c9F\u00cbG\u00cdH\u00cfI\u00d1J\u00d3K\u00d5", + "L\u00d7M\u00d9N\u00dbO\u00ddP\u00dfQ\u00e1R\u00e3S\u00e5T\u0003\u0002", + "\'\u0004\u0002CCcc\u0004\u0002DDdd\u0004\u0002EEee\u0004\u0002FFff\u0004", + "\u0002GGgg\u0004\u0002HHhh\u0004\u0002IIii\u0004\u0002JJjj\u0004\u0002", + "KKkk\u0004\u0002LLll\u0004\u0002MMmm\u0004\u0002NNnn\u0004\u0002OOo", + "o\u0004\u0002PPpp\u0004\u0002QQqq\u0004\u0002RRrr\u0004\u0002SSss\u0004", + "\u0002TTtt\u0004\u0002UUuu\u0004\u0002VVvv\u0004\u0002WWww\u0004\u0002", + "XXxx\u0004\u0002YYyy\u0004\u0002ZZzz\u0004\u0002[[{{\u0004\u0002\\\\", + "||\u0004\u00022;CH\u0003\u00022;\u0004\u0002$$^^\u0004\u0002))^^\u0004", + "\u0002^^bb\u0004\u0002\f\f\u000f\u000f\u0005\u0002\u000b\f\u000f\u000f", + "\"\"\u0005\u0002C\\aac|\u0006\u00022;C\\aac|\u0006\u0002C\\aac|\u00a3", + "\u0001\u0007\u00022;C\\aac|\u00a3\u0001\u0002\u0278\u0002C\u0003\u0002", + "\u0002\u0002\u0002E\u0003\u0002\u0002\u0002\u0002G\u0003\u0002\u0002", + "\u0002\u0002I\u0003\u0002\u0002\u0002\u0002K\u0003\u0002\u0002\u0002", + "\u0002M\u0003\u0002\u0002\u0002\u0002O\u0003\u0002\u0002\u0002\u0002", + "Q\u0003\u0002\u0002\u0002\u0002S\u0003\u0002\u0002\u0002\u0002U\u0003", + "\u0002\u0002\u0002\u0002W\u0003\u0002\u0002\u0002\u0002Y\u0003\u0002", + "\u0002\u0002\u0002[\u0003\u0002\u0002\u0002\u0002]\u0003\u0002\u0002", + "\u0002\u0002_\u0003\u0002\u0002\u0002\u0002a\u0003\u0002\u0002\u0002", + "\u0002c\u0003\u0002\u0002\u0002\u0002e\u0003\u0002\u0002\u0002\u0002", + "g\u0003\u0002\u0002\u0002\u0002i\u0003\u0002\u0002\u0002\u0002k\u0003", + "\u0002\u0002\u0002\u0002m\u0003\u0002\u0002\u0002\u0002o\u0003\u0002", + "\u0002\u0002\u0002q\u0003\u0002\u0002\u0002\u0002s\u0003\u0002\u0002", + "\u0002\u0002u\u0003\u0002\u0002\u0002\u0002w\u0003\u0002\u0002\u0002", + "\u0002y\u0003\u0002\u0002\u0002\u0002{\u0003\u0002\u0002\u0002\u0002", + "}\u0003\u0002\u0002\u0002\u0002\u007f\u0003\u0002\u0002\u0002\u0002", + "\u0081\u0003\u0002\u0002\u0002\u0002\u0083\u0003\u0002\u0002\u0002\u0002", + "\u0085\u0003\u0002\u0002\u0002\u0002\u0087\u0003\u0002\u0002\u0002\u0002", + "\u0089\u0003\u0002\u0002\u0002\u0002\u008b\u0003\u0002\u0002\u0002\u0002", + "\u008d\u0003\u0002\u0002\u0002\u0002\u008f\u0003\u0002\u0002\u0002\u0002", + "\u0091\u0003\u0002\u0002\u0002\u0002\u0093\u0003\u0002\u0002\u0002\u0002", + "\u0095\u0003\u0002\u0002\u0002\u0002\u0097\u0003\u0002\u0002\u0002\u0002", + "\u0099\u0003\u0002\u0002\u0002\u0002\u009b\u0003\u0002\u0002\u0002\u0002", + "\u009d\u0003\u0002\u0002\u0002\u0002\u009f\u0003\u0002\u0002\u0002\u0002", + "\u00a1\u0003\u0002\u0002\u0002\u0002\u00a3\u0003\u0002\u0002\u0002\u0002", + "\u00a5\u0003\u0002\u0002\u0002\u0002\u00a7\u0003\u0002\u0002\u0002\u0002", + "\u00a9\u0003\u0002\u0002\u0002\u0002\u00ab\u0003\u0002\u0002\u0002\u0002", + "\u00ad\u0003\u0002\u0002\u0002\u0002\u00af\u0003\u0002\u0002\u0002\u0002", + "\u00b1\u0003\u0002\u0002\u0002\u0002\u00b3\u0003\u0002\u0002\u0002\u0002", + "\u00b5\u0003\u0002\u0002\u0002\u0002\u00b7\u0003\u0002\u0002\u0002\u0002", + "\u00b9\u0003\u0002\u0002\u0002\u0002\u00bb\u0003\u0002\u0002\u0002\u0002", + "\u00bd\u0003\u0002\u0002\u0002\u0002\u00bf\u0003\u0002\u0002\u0002\u0002", + "\u00c1\u0003\u0002\u0002\u0002\u0002\u00c3\u0003\u0002\u0002\u0002\u0002", + "\u00c5\u0003\u0002\u0002\u0002\u0002\u00c7\u0003\u0002\u0002\u0002\u0002", + "\u00c9\u0003\u0002\u0002\u0002\u0002\u00cb\u0003\u0002\u0002\u0002\u0002", + "\u00cd\u0003\u0002\u0002\u0002\u0002\u00cf\u0003\u0002\u0002\u0002\u0002", + "\u00d1\u0003\u0002\u0002\u0002\u0002\u00d3\u0003\u0002\u0002\u0002\u0002", + "\u00d5\u0003\u0002\u0002\u0002\u0002\u00d7\u0003\u0002\u0002\u0002\u0002", + "\u00d9\u0003\u0002\u0002\u0002\u0002\u00db\u0003\u0002\u0002\u0002\u0002", + "\u00dd\u0003\u0002\u0002\u0002\u0002\u00df\u0003\u0002\u0002\u0002\u0002", + "\u00e1\u0003\u0002\u0002\u0002\u0002\u00e3\u0003\u0002\u0002\u0002\u0002", + "\u00e5\u0003\u0002\u0002\u0002\u0003\u00e7\u0003\u0002\u0002\u0002\u0005", + "\u00e9\u0003\u0002\u0002\u0002\u0007\u00eb\u0003\u0002\u0002\u0002\t", + "\u00ed\u0003\u0002\u0002\u0002\u000b\u00ef\u0003\u0002\u0002\u0002\r", + "\u00f1\u0003\u0002\u0002\u0002\u000f\u00f3\u0003\u0002\u0002\u0002\u0011", + "\u00f5\u0003\u0002\u0002\u0002\u0013\u00f7\u0003\u0002\u0002\u0002\u0015", + "\u00f9\u0003\u0002\u0002\u0002\u0017\u00fb\u0003\u0002\u0002\u0002\u0019", + "\u00fd\u0003\u0002\u0002\u0002\u001b\u00ff\u0003\u0002\u0002\u0002\u001d", + "\u0101\u0003\u0002\u0002\u0002\u001f\u0103\u0003\u0002\u0002\u0002!", + "\u0105\u0003\u0002\u0002\u0002#\u0107\u0003\u0002\u0002\u0002%\u0109", + "\u0003\u0002\u0002\u0002\'\u010b\u0003\u0002\u0002\u0002)\u010d\u0003", + "\u0002\u0002\u0002+\u010f\u0003\u0002\u0002\u0002-\u0111\u0003\u0002", + "\u0002\u0002/\u0113\u0003\u0002\u0002\u00021\u0115\u0003\u0002\u0002", + "\u00023\u0117\u0003\u0002\u0002\u00025\u0119\u0003\u0002\u0002\u0002", + "7\u011b\u0003\u0002\u0002\u00029\u011d\u0003\u0002\u0002\u0002;\u011f", + "\u0003\u0002\u0002\u0002=\u0121\u0003\u0002\u0002\u0002?\u012c\u0003", + "\u0002\u0002\u0002A\u0137\u0003\u0002\u0002\u0002C\u0144\u0003\u0002", + "\u0002\u0002E\u0150\u0003\u0002\u0002\u0002G\u015a\u0003\u0002\u0002", + "\u0002I\u015e\u0003\u0002\u0002\u0002K\u0163\u0003\u0002\u0002\u0002", + "M\u0169\u0003\u0002\u0002\u0002O\u016f\u0003\u0002\u0002\u0002Q\u017b", + "\u0003\u0002\u0002\u0002S\u017d\u0003\u0002\u0002\u0002U\u017f\u0003", + "\u0002\u0002\u0002W\u0182\u0003\u0002\u0002\u0002Y\u0184\u0003\u0002", + "\u0002\u0002[\u0187\u0003\u0002\u0002\u0002]\u0189\u0003\u0002\u0002", + "\u0002_\u018b\u0003\u0002\u0002\u0002a\u018d\u0003\u0002\u0002\u0002", + "c\u018f\u0003\u0002\u0002\u0002e\u0191\u0003\u0002\u0002\u0002g\u019b", + "\u0003\u0002\u0002\u0002i\u019f\u0003\u0002\u0002\u0002k\u01bb\u0003", + "\u0002\u0002\u0002m\u01ca\u0003\u0002\u0002\u0002o\u01cd\u0003\u0002", + "\u0002\u0002q\u01cf\u0003\u0002\u0002\u0002s\u01d1\u0003\u0002\u0002", + "\u0002u\u01d3\u0003\u0002\u0002\u0002w\u01da\u0003\u0002\u0002\u0002", + "y\u01e1\u0003\u0002\u0002\u0002{\u01e3\u0003\u0002\u0002\u0002}\u01e6", + "\u0003\u0002\u0002\u0002\u007f\u01e9\u0003\u0002\u0002\u0002\u0081\u01ec", + "\u0003\u0002\u0002\u0002\u0083\u01ef\u0003\u0002\u0002\u0002\u0085\u01f1", + "\u0003\u0002\u0002\u0002\u0087\u01f3\u0003\u0002\u0002\u0002\u0089\u01f6", + "\u0003\u0002\u0002\u0002\u008b\u01f9\u0003\u0002\u0002\u0002\u008d\u01fb", + "\u0003\u0002\u0002\u0002\u008f\u01fd\u0003\u0002\u0002\u0002\u0091\u0200", + "\u0003\u0002\u0002\u0002\u0093\u0202\u0003\u0002\u0002\u0002\u0095\u0205", + "\u0003\u0002\u0002\u0002\u0097\u0208\u0003\u0002\u0002\u0002\u0099\u020a", + "\u0003\u0002\u0002\u0002\u009b\u020d\u0003\u0002\u0002\u0002\u009d\u0210", + "\u0003\u0002\u0002\u0002\u009f\u0214\u0003\u0002\u0002\u0002\u00a1\u0217", + "\u0003\u0002\u0002\u0002\u00a3\u021a\u0003\u0002\u0002\u0002\u00a5\u021e", + "\u0003\u0002\u0002\u0002\u00a7\u0222\u0003\u0002\u0002\u0002\u00a9\u0224", + "\u0003\u0002\u0002\u0002\u00ab\u0227\u0003\u0002\u0002\u0002\u00ad\u022a", + "\u0003\u0002\u0002\u0002\u00af\u022d\u0003\u0002\u0002\u0002\u00b1\u0230", + "\u0003\u0002\u0002\u0002\u00b3\u0234\u0003\u0002\u0002\u0002\u00b5\u0237", + "\u0003\u0002\u0002\u0002\u00b7\u023b\u0003\u0002\u0002\u0002\u00b9\u023f", + "\u0003\u0002\u0002\u0002\u00bb\u0241\u0003\u0002\u0002\u0002\u00bd\u0243", + "\u0003\u0002\u0002\u0002\u00bf\u0245\u0003\u0002\u0002\u0002\u00c1\u0248", + "\u0003\u0002\u0002\u0002\u00c3\u024c\u0003\u0002\u0002\u0002\u00c5\u024f", + "\u0003\u0002\u0002\u0002\u00c7\u0251\u0003\u0002\u0002\u0002\u00c9\u0253", + "\u0003\u0002\u0002\u0002\u00cb\u0256\u0003\u0002\u0002\u0002\u00cd\u0259", + "\u0003\u0002\u0002\u0002\u00cf\u025c\u0003\u0002\u0002\u0002\u00d1\u025f", + "\u0003\u0002\u0002\u0002\u00d3\u0261\u0003\u0002\u0002\u0002\u00d5\u0263", + "\u0003\u0002\u0002\u0002\u00d7\u0266\u0003\u0002\u0002\u0002\u00d9\u026b", + "\u0003\u0002\u0002\u0002\u00db\u026f\u0003\u0002\u0002\u0002\u00dd\u0274", + "\u0003\u0002\u0002\u0002\u00df\u0278\u0003\u0002\u0002\u0002\u00e1\u027b", + "\u0003\u0002\u0002\u0002\u00e3\u027e\u0003\u0002\u0002\u0002\u00e5\u0280", + "\u0003\u0002\u0002\u0002\u00e7\u00e8\t\u0002\u0002\u0002\u00e8\u0004", + "\u0003\u0002\u0002\u0002\u00e9\u00ea\t\u0003\u0002\u0002\u00ea\u0006", + "\u0003\u0002\u0002\u0002\u00eb\u00ec\t\u0004\u0002\u0002\u00ec\b\u0003", + "\u0002\u0002\u0002\u00ed\u00ee\t\u0005\u0002\u0002\u00ee\n\u0003\u0002", + "\u0002\u0002\u00ef\u00f0\t\u0006\u0002\u0002\u00f0\f\u0003\u0002\u0002", + "\u0002\u00f1\u00f2\t\u0007\u0002\u0002\u00f2\u000e\u0003\u0002\u0002", + "\u0002\u00f3\u00f4\t\b\u0002\u0002\u00f4\u0010\u0003\u0002\u0002\u0002", + "\u00f5\u00f6\t\t\u0002\u0002\u00f6\u0012\u0003\u0002\u0002\u0002\u00f7", + "\u00f8\t\n\u0002\u0002\u00f8\u0014\u0003\u0002\u0002\u0002\u00f9\u00fa", + "\t\u000b\u0002\u0002\u00fa\u0016\u0003\u0002\u0002\u0002\u00fb\u00fc", + "\t\f\u0002\u0002\u00fc\u0018\u0003\u0002\u0002\u0002\u00fd\u00fe\t\r", + "\u0002\u0002\u00fe\u001a\u0003\u0002\u0002\u0002\u00ff\u0100\t\u000e", + "\u0002\u0002\u0100\u001c\u0003\u0002\u0002\u0002\u0101\u0102\t\u000f", + "\u0002\u0002\u0102\u001e\u0003\u0002\u0002\u0002\u0103\u0104\t\u0010", + "\u0002\u0002\u0104 \u0003\u0002\u0002\u0002\u0105\u0106\t\u0011\u0002", + "\u0002\u0106\"\u0003\u0002\u0002\u0002\u0107\u0108\t\u0012\u0002\u0002", + "\u0108$\u0003\u0002\u0002\u0002\u0109\u010a\t\u0013\u0002\u0002\u010a", + "&\u0003\u0002\u0002\u0002\u010b\u010c\t\u0014\u0002\u0002\u010c(\u0003", + "\u0002\u0002\u0002\u010d\u010e\t\u0015\u0002\u0002\u010e*\u0003\u0002", + "\u0002\u0002\u010f\u0110\t\u0016\u0002\u0002\u0110,\u0003\u0002\u0002", + "\u0002\u0111\u0112\t\u0017\u0002\u0002\u0112.\u0003\u0002\u0002\u0002", + "\u0113\u0114\t\u0018\u0002\u0002\u01140\u0003\u0002\u0002\u0002\u0115", + "\u0116\t\u0019\u0002\u0002\u01162\u0003\u0002\u0002\u0002\u0117\u0118", + "\t\u001a\u0002\u0002\u01184\u0003\u0002\u0002\u0002\u0119\u011a\t\u001b", + "\u0002\u0002\u011a6\u0003\u0002\u0002\u0002\u011b\u011c\u0007a\u0002", + "\u0002\u011c8\u0003\u0002\u0002\u0002\u011d\u011e\t\u001c\u0002\u0002", + "\u011e:\u0003\u0002\u0002\u0002\u011f\u0120\t\u001d\u0002\u0002\u0120", + "<\u0003\u0002\u0002\u0002\u0121\u0127\u0007$\u0002\u0002\u0122\u0123", + "\u0007^\u0002\u0002\u0123\u0126\u000b\u0002\u0002\u0002\u0124\u0126", + "\n\u001e\u0002\u0002\u0125\u0122\u0003\u0002\u0002\u0002\u0125\u0124", + "\u0003\u0002\u0002\u0002\u0126\u0129\u0003\u0002\u0002\u0002\u0127\u0125", + "\u0003\u0002\u0002\u0002\u0127\u0128\u0003\u0002\u0002\u0002\u0128\u012a", + "\u0003\u0002\u0002\u0002\u0129\u0127\u0003\u0002\u0002\u0002\u012a\u012b", + "\u0007$\u0002\u0002\u012b>\u0003\u0002\u0002\u0002\u012c\u0132\u0007", + ")\u0002\u0002\u012d\u012e\u0007^\u0002\u0002\u012e\u0131\u000b\u0002", + "\u0002\u0002\u012f\u0131\n\u001f\u0002\u0002\u0130\u012d\u0003\u0002", + "\u0002\u0002\u0130\u012f\u0003\u0002\u0002\u0002\u0131\u0134\u0003\u0002", + "\u0002\u0002\u0132\u0130\u0003\u0002\u0002\u0002\u0132\u0133\u0003\u0002", + "\u0002\u0002\u0133\u0135\u0003\u0002\u0002\u0002\u0134\u0132\u0003\u0002", + "\u0002\u0002\u0135\u0136\u0007)\u0002\u0002\u0136@\u0003\u0002\u0002", + "\u0002\u0137\u013f\u0007b\u0002\u0002\u0138\u0139\u0007^\u0002\u0002", + "\u0139\u013e\u000b\u0002\u0002\u0002\u013a\u013b\u0007b\u0002\u0002", + "\u013b\u013e\u0007b\u0002\u0002\u013c\u013e\n \u0002\u0002\u013d\u0138", + "\u0003\u0002\u0002\u0002\u013d\u013a\u0003\u0002\u0002\u0002\u013d\u013c", + "\u0003\u0002\u0002\u0002\u013e\u0141\u0003\u0002\u0002\u0002\u013f\u013d", + "\u0003\u0002\u0002\u0002\u013f\u0140\u0003\u0002\u0002\u0002\u0140\u0142", + "\u0003\u0002\u0002\u0002\u0141\u013f\u0003\u0002\u0002\u0002\u0142\u0143", + "\u0007b\u0002\u0002\u0143B\u0003\u0002\u0002\u0002\u0144\u0145\u0007", + "1\u0002\u0002\u0145\u0146\u0007,\u0002\u0002\u0146\u014a\u0003\u0002", + "\u0002\u0002\u0147\u0149\u000b\u0002\u0002\u0002\u0148\u0147\u0003\u0002", + "\u0002\u0002\u0149\u014c\u0003\u0002\u0002\u0002\u014a\u0148\u0003\u0002", + "\u0002\u0002\u014a\u014b\u0003\u0002\u0002\u0002\u014b\u014d\u0003\u0002", + "\u0002\u0002\u014c\u014a\u0003\u0002\u0002\u0002\u014d\u014e\u0007,", + "\u0002\u0002\u014e\u014f\u00071\u0002\u0002\u014fD\u0003\u0002\u0002", + "\u0002\u0150\u0151\u00071\u0002\u0002\u0151\u0152\u00071\u0002\u0002", + "\u0152\u0156\u0003\u0002\u0002\u0002\u0153\u0155\n!\u0002\u0002\u0154", + "\u0153\u0003\u0002\u0002\u0002\u0155\u0158\u0003\u0002\u0002\u0002\u0156", + "\u0154\u0003\u0002\u0002\u0002\u0156\u0157\u0003\u0002\u0002\u0002\u0157", + "F\u0003\u0002\u0002\u0002\u0158\u0156\u0003\u0002\u0002\u0002\u0159", + "\u015b\t\"\u0002\u0002\u015a\u0159\u0003\u0002\u0002\u0002\u015b\u015c", + "\u0003\u0002\u0002\u0002\u015c\u015a\u0003\u0002\u0002\u0002\u015c\u015d", + "\u0003\u0002\u0002\u0002\u015dH\u0003\u0002\u0002\u0002\u015e\u015f", + "\u0005)\u0015\u0002\u015f\u0160\u0005%\u0013\u0002\u0160\u0161\u0005", + "+\u0016\u0002\u0161\u0162\u0005\u000b\u0006\u0002\u0162J\u0003\u0002", + "\u0002\u0002\u0163\u0164\u0005\r\u0007\u0002\u0164\u0165\u0005\u0003", + "\u0002\u0002\u0165\u0166\u0005\u0019\r\u0002\u0166\u0167\u0005\'\u0014", + "\u0002\u0167\u0168\u0005\u000b\u0006\u0002\u0168L\u0003\u0002\u0002", + "\u0002\u0169\u016a\u0005\r\u0007\u0002\u016a\u016b\u0005\u0013\n\u0002", + "\u016b\u016c\u0005\u000b\u0006\u0002\u016c\u016d\u0005\u0019\r\u0002", + "\u016d\u016e\u0005\t\u0005\u0002\u016eN\u0003\u0002\u0002\u0002\u016f", + "\u0170\u0005\r\u0007\u0002\u0170\u0171\u0005\u0013\n\u0002\u0171\u0172", + "\u0005\u000b\u0006\u0002\u0172\u0173\u0005\u0019\r\u0002\u0173\u0174", + "\u0005\t\u0005\u0002\u0174\u0175\u00057\u001c\u0002\u0175\u0176\u0005", + "\u0005\u0003\u0002\u0176\u0177\u00053\u001a\u0002\u0177\u0178\u0005", + "7\u001c\u0002\u0178\u0179\u0005\u0013\n\u0002\u0179\u017a\u0005\t\u0005", + "\u0002\u017aP\u0003\u0002\u0002\u0002\u017b\u017c\u0007.\u0002\u0002", + "\u017cR\u0003\u0002\u0002\u0002\u017d\u017e\u0007<\u0002\u0002\u017e", + "T\u0003\u0002\u0002\u0002\u017f\u0180\u0007<\u0002\u0002\u0180\u0181", + "\u0007<\u0002\u0002\u0181V\u0003\u0002\u0002\u0002\u0182\u0183\u0007", + "&\u0002\u0002\u0183X\u0003\u0002\u0002\u0002\u0184\u0185\u0007&\u0002", + "\u0002\u0185\u0186\u0007&\u0002\u0002\u0186Z\u0003\u0002\u0002\u0002", + "\u0187\u0188\u0007,\u0002\u0002\u0188\\\u0003\u0002\u0002\u0002\u0189", + "\u018a\u0007*\u0002\u0002\u018a^\u0003\u0002\u0002\u0002\u018b\u018c", + "\u0007+\u0002\u0002\u018c`\u0003\u0002\u0002\u0002\u018d\u018e\u0007", + "]\u0002\u0002\u018eb\u0003\u0002\u0002\u0002\u018f\u0190\u0007_\u0002", + "\u0002\u0190d\u0003\u0002\u0002\u0002\u0191\u0192\u0005\u0005\u0003", + "\u0002\u0192\u0196\u0007)\u0002\u0002\u0193\u0195\u000423\u0002\u0194", + "\u0193\u0003\u0002\u0002\u0002\u0195\u0198\u0003\u0002\u0002\u0002\u0196", + "\u0194\u0003\u0002\u0002\u0002\u0196\u0197\u0003\u0002\u0002\u0002\u0197", + "\u0199\u0003\u0002\u0002\u0002\u0198\u0196\u0003\u0002\u0002\u0002\u0199", + "\u019a\u0007)\u0002\u0002\u019af\u0003\u0002\u0002\u0002\u019b\u019c", + "\u0005\u000b\u0006\u0002\u019c\u019d\u0005? \u0002\u019dh\u0003\u0002", + "\u0002\u0002\u019e\u01a0\u0007/\u0002\u0002\u019f\u019e\u0003\u0002", + "\u0002\u0002\u019f\u01a0\u0003\u0002\u0002\u0002\u01a0\u01a2\u0003\u0002", + "\u0002\u0002\u01a1\u01a3\u0005;\u001e\u0002\u01a2\u01a1\u0003\u0002", + "\u0002\u0002\u01a3\u01a4\u0003\u0002\u0002\u0002\u01a4\u01a2\u0003\u0002", + "\u0002\u0002\u01a4\u01a5\u0003\u0002\u0002\u0002\u01a5\u01a6\u0003\u0002", + "\u0002\u0002\u01a6\u01a8\u00070\u0002\u0002\u01a7\u01a9\u0005;\u001e", + "\u0002\u01a8\u01a7\u0003\u0002\u0002\u0002\u01a9\u01aa\u0003\u0002\u0002", + "\u0002\u01aa\u01a8\u0003\u0002\u0002\u0002\u01aa\u01ab\u0003\u0002\u0002", + "\u0002\u01ab\u01b8\u0003\u0002\u0002\u0002\u01ac\u01b0\u0005\u000b\u0006", + "\u0002\u01ad\u01af\u0007/\u0002\u0002\u01ae\u01ad\u0003\u0002\u0002", + "\u0002\u01af\u01b2\u0003\u0002\u0002\u0002\u01b0\u01ae\u0003\u0002\u0002", + "\u0002\u01b0\u01b1\u0003\u0002\u0002\u0002\u01b1\u01b4\u0003\u0002\u0002", + "\u0002\u01b2\u01b0\u0003\u0002\u0002\u0002\u01b3\u01b5\u0005;\u001e", + "\u0002\u01b4\u01b3\u0003\u0002\u0002\u0002\u01b5\u01b6\u0003\u0002\u0002", + "\u0002\u01b6\u01b4\u0003\u0002\u0002\u0002\u01b6\u01b7\u0003\u0002\u0002", + "\u0002\u01b7\u01b9\u0003\u0002\u0002\u0002\u01b8\u01ac\u0003\u0002\u0002", + "\u0002\u01b8\u01b9\u0003\u0002\u0002\u0002\u01b9j\u0003\u0002\u0002", + "\u0002\u01ba\u01bc\u0007/\u0002\u0002\u01bb\u01ba\u0003\u0002\u0002", + "\u0002\u01bb\u01bc\u0003\u0002\u0002\u0002\u01bc\u01be\u0003\u0002\u0002", + "\u0002\u01bd\u01bf\u0005;\u001e\u0002\u01be\u01bd\u0003\u0002\u0002", + "\u0002\u01bf\u01c0\u0003\u0002\u0002\u0002\u01c0\u01be\u0003\u0002\u0002", + "\u0002\u01c0\u01c1\u0003\u0002\u0002\u0002\u01c1\u01c8\u0003\u0002\u0002", + "\u0002\u01c2\u01c4\u0005\u000b\u0006\u0002\u01c3\u01c5\u0005;\u001e", + "\u0002\u01c4\u01c3\u0003\u0002\u0002\u0002\u01c5\u01c6\u0003\u0002\u0002", + "\u0002\u01c6\u01c4\u0003\u0002\u0002\u0002\u01c6\u01c7\u0003\u0002\u0002", + "\u0002\u01c7\u01c9\u0003\u0002\u0002\u0002\u01c8\u01c2\u0003\u0002\u0002", + "\u0002\u01c8\u01c9\u0003\u0002\u0002\u0002\u01c9l\u0003\u0002\u0002", + "\u0002\u01ca\u01cb\u0007z\u0002\u0002\u01cb\u01cc\u0005? \u0002\u01cc", + "n\u0003\u0002\u0002\u0002\u01cd\u01ce\u00070\u0002\u0002\u01cep\u0003", + "\u0002\u0002\u0002\u01cf\u01d0\u0005? \u0002\u01d0r\u0003\u0002\u0002", + "\u0002\u01d1\u01d2\u0005=\u001f\u0002\u01d2t\u0003\u0002\u0002\u0002", + "\u01d3\u01d7\t#\u0002\u0002\u01d4\u01d6\t$\u0002\u0002\u01d5\u01d4\u0003", + "\u0002\u0002\u0002\u01d6\u01d9\u0003\u0002\u0002\u0002\u01d7\u01d5\u0003", + "\u0002\u0002\u0002\u01d7\u01d8\u0003\u0002\u0002\u0002\u01d8v\u0003", + "\u0002\u0002\u0002\u01d9\u01d7\u0003\u0002\u0002\u0002\u01da\u01de\t", + "%\u0002\u0002\u01db\u01dd\t&\u0002\u0002\u01dc\u01db\u0003\u0002\u0002", + "\u0002\u01dd\u01e0\u0003\u0002\u0002\u0002\u01de\u01dc\u0003\u0002\u0002", + "\u0002\u01de\u01df\u0003\u0002\u0002\u0002\u01dfx\u0003\u0002\u0002", + "\u0002\u01e0\u01de\u0003\u0002\u0002\u0002\u01e1\u01e2\u0007(\u0002", + "\u0002\u01e2z\u0003\u0002\u0002\u0002\u01e3\u01e4\u0007(\u0002\u0002", + "\u01e4\u01e5\u0007(\u0002\u0002\u01e5|\u0003\u0002\u0002\u0002\u01e6", + "\u01e7\u0007(\u0002\u0002\u01e7\u01e8\u0007>\u0002\u0002\u01e8~\u0003", + "\u0002\u0002\u0002\u01e9\u01ea\u0007B\u0002\u0002\u01ea\u01eb\u0007", + "B\u0002\u0002\u01eb\u0080\u0003\u0002\u0002\u0002\u01ec\u01ed\u0007", + "B\u0002\u0002\u01ed\u01ee\u0007@\u0002\u0002\u01ee\u0082\u0003\u0002", + "\u0002\u0002\u01ef\u01f0\u0007B\u0002\u0002\u01f0\u0084\u0003\u0002", + "\u0002\u0002\u01f1\u01f2\u0007#\u0002\u0002\u01f2\u0086\u0003\u0002", + "\u0002\u0002\u01f3\u01f4\u0007#\u0002\u0002\u01f4\u01f5\u0007#\u0002", + "\u0002\u01f5\u0088\u0003\u0002\u0002\u0002\u01f6\u01f7\u0007#\u0002", + "\u0002\u01f7\u01f8\u0007?\u0002\u0002\u01f8\u008a\u0003\u0002\u0002", + "\u0002\u01f9\u01fa\u0007`\u0002\u0002\u01fa\u008c\u0003\u0002\u0002", + "\u0002\u01fb\u01fc\u0007?\u0002\u0002\u01fc\u008e\u0003\u0002\u0002", + "\u0002\u01fd\u01fe\u0007?\u0002\u0002\u01fe\u01ff\u0007@\u0002\u0002", + "\u01ff\u0090\u0003\u0002\u0002\u0002\u0200\u0201\u0007@\u0002\u0002", + "\u0201\u0092\u0003\u0002\u0002\u0002\u0202\u0203\u0007@\u0002\u0002", + "\u0203\u0204\u0007?\u0002\u0002\u0204\u0094\u0003\u0002\u0002\u0002", + "\u0205\u0206\u0007@\u0002\u0002\u0206\u0207\u0007@\u0002\u0002\u0207", + "\u0096\u0003\u0002\u0002\u0002\u0208\u0209\u0007%\u0002\u0002\u0209", + "\u0098\u0003\u0002\u0002\u0002\u020a\u020b\u0007%\u0002\u0002\u020b", + "\u020c\u0007?\u0002\u0002\u020c\u009a\u0003\u0002\u0002\u0002\u020d", + "\u020e\u0007%\u0002\u0002\u020e\u020f\u0007@\u0002\u0002\u020f\u009c", + "\u0003\u0002\u0002\u0002\u0210\u0211\u0007%\u0002\u0002\u0211\u0212", + "\u0007@\u0002\u0002\u0212\u0213\u0007@\u0002\u0002\u0213\u009e\u0003", + "\u0002\u0002\u0002\u0214\u0215\u0007%\u0002\u0002\u0215\u0216\u0007", + "%\u0002\u0002\u0216\u00a0\u0003\u0002\u0002\u0002\u0217\u0218\u0007", + "/\u0002\u0002\u0218\u0219\u0007@\u0002\u0002\u0219\u00a2\u0003\u0002", + "\u0002\u0002\u021a\u021b\u0007/\u0002\u0002\u021b\u021c\u0007@\u0002", + "\u0002\u021c\u021d\u0007@\u0002\u0002\u021d\u00a4\u0003\u0002\u0002", + "\u0002\u021e\u021f\u0007/\u0002\u0002\u021f\u0220\u0007~\u0002\u0002", + "\u0220\u0221\u0007/\u0002\u0002\u0221\u00a6\u0003\u0002\u0002\u0002", + "\u0222\u0223\u0007>\u0002\u0002\u0223\u00a8\u0003\u0002\u0002\u0002", + "\u0224\u0225\u0007>\u0002\u0002\u0225\u0226\u0007?\u0002\u0002\u0226", + "\u00aa\u0003\u0002\u0002\u0002\u0227\u0228\u0007>\u0002\u0002\u0228", + "\u0229\u0007B\u0002\u0002\u0229\u00ac\u0003\u0002\u0002\u0002\u022a", + "\u022b\u0007>\u0002\u0002\u022b\u022c\u0007`\u0002\u0002\u022c\u00ae", + "\u0003\u0002\u0002\u0002\u022d\u022e\u0007>\u0002\u0002\u022e\u022f", + "\u0007@\u0002\u0002\u022f\u00b0\u0003\u0002\u0002\u0002\u0230\u0231", + "\u0007>\u0002\u0002\u0231\u0232\u0007/\u0002\u0002\u0232\u0233\u0007", + "@\u0002\u0002\u0233\u00b2\u0003\u0002\u0002\u0002\u0234\u0235\u0007", + ">\u0002\u0002\u0235\u0236\u0007>\u0002\u0002\u0236\u00b4\u0003\u0002", + "\u0002\u0002\u0237\u0238\u0007>\u0002\u0002\u0238\u0239\u0007>\u0002", + "\u0002\u0239\u023a\u0007?\u0002\u0002\u023a\u00b6\u0003\u0002\u0002", + "\u0002\u023b\u023c\u0007>\u0002\u0002\u023c\u023d\u0007A\u0002\u0002", + "\u023d\u023e\u0007@\u0002\u0002\u023e\u00b8\u0003\u0002\u0002\u0002", + "\u023f\u0240\u0007/\u0002\u0002\u0240\u00ba\u0003\u0002\u0002\u0002", + "\u0241\u0242\u0007\'\u0002\u0002\u0242\u00bc\u0003\u0002\u0002\u0002", + "\u0243\u0244\u0007~\u0002\u0002\u0244\u00be\u0003\u0002\u0002\u0002", + "\u0245\u0246\u0007~\u0002\u0002\u0246\u0247\u0007~\u0002\u0002\u0247", + "\u00c0\u0003\u0002\u0002\u0002\u0248\u0249\u0007~\u0002\u0002\u0249", + "\u024a\u0007~\u0002\u0002\u024a\u024b\u00071\u0002\u0002\u024b\u00c2", + "\u0003\u0002\u0002\u0002\u024c\u024d\u0007~\u0002\u0002\u024d\u024e", + "\u00071\u0002\u0002\u024e\u00c4\u0003\u0002\u0002\u0002\u024f\u0250", + "\u0007-\u0002\u0002\u0250\u00c6\u0003\u0002\u0002\u0002\u0251\u0252", + "\u0007A\u0002\u0002\u0252\u00c8\u0003\u0002\u0002\u0002\u0253\u0254", + "\u0007A\u0002\u0002\u0254\u0255\u0007(\u0002\u0002\u0255\u00ca\u0003", + "\u0002\u0002\u0002\u0256\u0257\u0007A\u0002\u0002\u0257\u0258\u0007", + "%\u0002\u0002\u0258\u00cc\u0003\u0002\u0002\u0002\u0259\u025a\u0007", + "A\u0002\u0002\u025a\u025b\u0007/\u0002\u0002\u025b\u00ce\u0003\u0002", + "\u0002\u0002\u025c\u025d\u0007A\u0002\u0002\u025d\u025e\u0007~\u0002", + "\u0002\u025e\u00d0\u0003\u0002\u0002\u0002\u025f\u0260\u00071\u0002", + "\u0002\u0260\u00d2\u0003\u0002\u0002\u0002\u0261\u0262\u0007\u0080\u0002", + "\u0002\u0262\u00d4\u0003\u0002\u0002\u0002\u0263\u0264\u0007\u0080\u0002", + "\u0002\u0264\u0265\u0007?\u0002\u0002\u0265\u00d6\u0003\u0002\u0002", + "\u0002\u0266\u0267\u0007\u0080\u0002\u0002\u0267\u0268\u0007@\u0002", + "\u0002\u0268\u0269\u0007?\u0002\u0002\u0269\u026a\u0007\u0080\u0002", + "\u0002\u026a\u00d8\u0003\u0002\u0002\u0002\u026b\u026c\u0007\u0080\u0002", + "\u0002\u026c\u026d\u0007@\u0002\u0002\u026d\u026e\u0007\u0080\u0002", + "\u0002\u026e\u00da\u0003\u0002\u0002\u0002\u026f\u0270\u0007\u0080\u0002", + "\u0002\u0270\u0271\u0007>\u0002\u0002\u0271\u0272\u0007?\u0002\u0002", + "\u0272\u0273\u0007\u0080\u0002\u0002\u0273\u00dc\u0003\u0002\u0002\u0002", + "\u0274\u0275\u0007\u0080\u0002\u0002\u0275\u0276\u0007>\u0002\u0002", + "\u0276\u0277\u0007\u0080\u0002\u0002\u0277\u00de\u0003\u0002\u0002\u0002", + "\u0278\u0279\u0007\u0080\u0002\u0002\u0279\u027a\u0007,\u0002\u0002", + "\u027a\u00e0\u0003\u0002\u0002\u0002\u027b\u027c\u0007\u0080\u0002\u0002", + "\u027c\u027d\u0007\u0080\u0002\u0002\u027d\u00e2\u0003\u0002\u0002\u0002", + "\u027e\u027f\u0007=\u0002\u0002\u027f\u00e4\u0003\u0002\u0002\u0002", + "\u0280\u0281\u000b\u0002\u0002\u0002\u0281\u00e6\u0003\u0002\u0002\u0002", + "\u0019\u0002\u0125\u0127\u0130\u0132\u013d\u013f\u014a\u0156\u015c\u0196", + "\u019f\u01a4\u01aa\u01b0\u01b6\u01b8\u01bb\u01c0\u01c6\u01c8\u01d7\u01de", + "\u0002"].join(""); var atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN); @@ -428,9 +423,9 @@ Object.defineProperty(BaserowFormulaLexer.prototype, "atn", { }); BaserowFormulaLexer.EOF = antlr4.Token.EOF; -BaserowFormulaLexer.WHITESPACE = 1; -BaserowFormulaLexer.BLOCK_COMMENT = 2; -BaserowFormulaLexer.LINE_COMMENT = 3; +BaserowFormulaLexer.BLOCK_COMMENT = 1; +BaserowFormulaLexer.LINE_COMMENT = 2; +BaserowFormulaLexer.WHITESPACE = 3; BaserowFormulaLexer.TRUE = 4; BaserowFormulaLexer.FALSE = 5; BaserowFormulaLexer.FIELD = 6; @@ -538,10 +533,10 @@ BaserowFormulaLexer.prototype.literalNames = [ null, null, null, null, null, "'~>~'", "'~<=~'", "'~<~'", "'~*'", "'~~'", "';'" ]; -BaserowFormulaLexer.prototype.symbolicNames = [ null, "WHITESPACE", "BLOCK_COMMENT", - "LINE_COMMENT", "TRUE", - "FALSE", "FIELD", "FIELDBYID", - "COMMA", "COLON", "COLON_COLON", +BaserowFormulaLexer.prototype.symbolicNames = [ null, "BLOCK_COMMENT", "LINE_COMMENT", + "WHITESPACE", "TRUE", "FALSE", + "FIELD", "FIELDBYID", "COMMA", + "COLON", "COLON_COLON", "DOLLAR", "DOLLAR_DOLLAR", "STAR", "OPEN_PAREN", "CLOSE_PAREN", "OPEN_BRACKET", "CLOSE_BRACKET", @@ -573,31 +568,32 @@ BaserowFormulaLexer.prototype.symbolicNames = [ null, "WHITESPACE", "BLOCK_COMME "TIL_STAR", "TIL_TIL", "SEMI", "ErrorCharacter" ]; -BaserowFormulaLexer.prototype.ruleNames = [ "WHITESPACE", "BLOCK_COMMENT", - "LINE_COMMENT", "A", "B", "C", - "D", "E", "F", "G", "H", "I", - "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", - "V", "W", "X", "Y", "Z", "UNDERSCORE", - "HEX_DIGIT", "DEC_DIGIT", "DQUOTA_STRING", +BaserowFormulaLexer.prototype.ruleNames = [ "A", "B", "C", "D", "E", "F", + "G", "H", "I", "J", "K", "L", + "M", "N", "O", "P", "Q", "R", + "S", "T", "U", "V", "W", "X", + "Y", "Z", "UNDERSCORE", "HEX_DIGIT", + "DEC_DIGIT", "DQUOTA_STRING", "SQUOTA_STRING", "BQUOTA_STRING", - "TRUE", "FALSE", "FIELD", "FIELDBYID", - "COMMA", "COLON", "COLON_COLON", - "DOLLAR", "DOLLAR_DOLLAR", "STAR", - "OPEN_PAREN", "CLOSE_PAREN", - "OPEN_BRACKET", "CLOSE_BRACKET", - "BIT_STRING", "REGEX_STRING", - "NUMERIC_LITERAL", "INTEGER_LITERAL", - "HEX_INTEGER_LITERAL", "DOT", - "SINGLEQ_STRING_LITERAL", "DOUBLEQ_STRING_LITERAL", - "IDENTIFIER", "IDENTIFIER_UNICODE", - "AMP", "AMP_AMP", "AMP_LT", - "AT_AT", "AT_GT", "AT_SIGN", - "BANG", "BANG_BANG", "BANG_EQUAL", - "CARET", "EQUAL", "EQUAL_GT", - "GT", "GTE", "GT_GT", "HASH", - "HASH_EQ", "HASH_GT", "HASH_GT_GT", - "HASH_HASH", "HYPHEN_GT", "HYPHEN_GT_GT", + "BLOCK_COMMENT", "LINE_COMMENT", + "WHITESPACE", "TRUE", "FALSE", + "FIELD", "FIELDBYID", "COMMA", + "COLON", "COLON_COLON", "DOLLAR", + "DOLLAR_DOLLAR", "STAR", "OPEN_PAREN", + "CLOSE_PAREN", "OPEN_BRACKET", + "CLOSE_BRACKET", "BIT_STRING", + "REGEX_STRING", "NUMERIC_LITERAL", + "INTEGER_LITERAL", "HEX_INTEGER_LITERAL", + "DOT", "SINGLEQ_STRING_LITERAL", + "DOUBLEQ_STRING_LITERAL", "IDENTIFIER", + "IDENTIFIER_UNICODE", "AMP", + "AMP_AMP", "AMP_LT", "AT_AT", + "AT_GT", "AT_SIGN", "BANG", + "BANG_BANG", "BANG_EQUAL", "CARET", + "EQUAL", "EQUAL_GT", "GT", "GTE", + "GT_GT", "HASH", "HASH_EQ", + "HASH_GT", "HASH_GT_GT", "HASH_HASH", + "HYPHEN_GT", "HYPHEN_GT_GT", "HYPHEN_PIPE_HYPHEN", "LT", "LTE", "LT_AT", "LT_CARET", "LT_GT", "LT_HYPHEN_GT", "LT_LT", diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.tokens b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.tokens index 805982389..1b6673037 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.tokens +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaLexer.tokens @@ -1,6 +1,6 @@ -WHITESPACE=1 -BLOCK_COMMENT=2 -LINE_COMMENT=3 +BLOCK_COMMENT=1 +LINE_COMMENT=2 +WHITESPACE=3 TRUE=4 FALSE=5 FIELD=6 diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaListener.js b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaListener.js index d5b6c6309..8f61176f4 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaListener.js +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaListener.js @@ -56,6 +56,15 @@ BaserowFormulaListener.prototype.exitBooleanLiteral = function(ctx) { }; +// Enter a parse tree produced by BaserowFormula#RightWhitespaceOrComments. +BaserowFormulaListener.prototype.enterRightWhitespaceOrComments = function(ctx) { +}; + +// Exit a parse tree produced by BaserowFormula#RightWhitespaceOrComments. +BaserowFormulaListener.prototype.exitRightWhitespaceOrComments = function(ctx) { +}; + + // Enter a parse tree produced by BaserowFormula#DecimalLiteral. BaserowFormulaListener.prototype.enterDecimalLiteral = function(ctx) { }; @@ -65,6 +74,15 @@ BaserowFormulaListener.prototype.exitDecimalLiteral = function(ctx) { }; +// Enter a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. +BaserowFormulaListener.prototype.enterLeftWhitespaceOrComments = function(ctx) { +}; + +// Exit a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. +BaserowFormulaListener.prototype.exitLeftWhitespaceOrComments = function(ctx) { +}; + + // Enter a parse tree produced by BaserowFormula#FunctionCall. BaserowFormulaListener.prototype.enterFunctionCall = function(ctx) { }; @@ -101,6 +119,15 @@ BaserowFormulaListener.prototype.exitBinaryOp = function(ctx) { }; +// Enter a parse tree produced by BaserowFormula#ws_or_comment. +BaserowFormulaListener.prototype.enterWs_or_comment = function(ctx) { +}; + +// Exit a parse tree produced by BaserowFormula#ws_or_comment. +BaserowFormulaListener.prototype.exitWs_or_comment = function(ctx) { +}; + + // Enter a parse tree produced by BaserowFormula#func_name. BaserowFormulaListener.prototype.enterFunc_name = function(ctx) { }; diff --git a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaVisitor.js b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaVisitor.js index a0d7eb1a2..a58442d1c 100644 --- a/web-frontend/modules/database/formula/parser/generated/BaserowFormulaVisitor.js +++ b/web-frontend/modules/database/formula/parser/generated/BaserowFormulaVisitor.js @@ -42,12 +42,24 @@ BaserowFormulaVisitor.prototype.visitBooleanLiteral = function(ctx) { }; +// Visit a parse tree produced by BaserowFormula#RightWhitespaceOrComments. +BaserowFormulaVisitor.prototype.visitRightWhitespaceOrComments = function(ctx) { + return this.visitChildren(ctx); +}; + + // Visit a parse tree produced by BaserowFormula#DecimalLiteral. BaserowFormulaVisitor.prototype.visitDecimalLiteral = function(ctx) { return this.visitChildren(ctx); }; +// Visit a parse tree produced by BaserowFormula#LeftWhitespaceOrComments. +BaserowFormulaVisitor.prototype.visitLeftWhitespaceOrComments = function(ctx) { + return this.visitChildren(ctx); +}; + + // Visit a parse tree produced by BaserowFormula#FunctionCall. BaserowFormulaVisitor.prototype.visitFunctionCall = function(ctx) { return this.visitChildren(ctx); @@ -72,6 +84,12 @@ BaserowFormulaVisitor.prototype.visitBinaryOp = function(ctx) { }; +// Visit a parse tree produced by BaserowFormula#ws_or_comment. +BaserowFormulaVisitor.prototype.visitWs_or_comment = function(ctx) { + return this.visitChildren(ctx); +}; + + // Visit a parse tree produced by BaserowFormula#func_name. BaserowFormulaVisitor.prototype.visitFunc_name = function(ctx) { return this.visitChildren(ctx); diff --git a/web-frontend/modules/database/formula/parser/replaceFieldByIdWithField.js b/web-frontend/modules/database/formula/parser/replaceFieldByIdWithField.js deleted file mode 100644 index a6df7c2fa..000000000 --- a/web-frontend/modules/database/formula/parser/replaceFieldByIdWithField.js +++ /dev/null @@ -1,105 +0,0 @@ -import { BaserowFormulaLexer } from '@baserow/modules/database/formula/parser/generated/BaserowFormulaLexer' -import { getTokenStreamForFormula } from '@baserow/modules/database/formula/parser/parser' - -/** - * Given a map of field id to field name replaces all field_by_id references to - * with field references. Does so whist preserving any whitespace or - * comments. If a reference to an unknown field_by_id is made it will be left as it is. - * - * This algorithm is duplicated in the backend in replace_field_by_id_with_field.py - * please sync across any changes. - * - * @param formula The raw string to tokenize and transform. - * @param fieldIdToName The map of field ids to names. - * @returns string False if the formula is not - * syntactically correct, otherwise the new updated formula string. - */ -export function replaceFieldByIdWithField(formula, fieldIdToName) { - const stream = getTokenStreamForFormula(formula) - - let searchingForOpenParen = false - let searchingForCloseParen = false - let searchingForIntegerLiteral = false - - let newFormula = '' - for (let i = 0; i < stream.tokens.length; i++) { - const token = stream.tokens[i] - let output = token.text - - const isNormalToken = token.channel === 0 - if (isNormalToken) { - if (searchingForIntegerLiteral) { - searchingForIntegerLiteral = false - searchingForCloseParen = true - if (token.type === BaserowFormulaLexer.INTEGER_LITERAL) { - output = `'${fieldIdToName[output].replace("'", "\\'")}'` - } else { - // The only valid normal token is an int, we've encountered a different - // token and hence the input string is invalid and so we'll just return it - // untouched. - return formula - } - } else if (searchingForOpenParen) { - searchingForOpenParen = false - if (token.type === BaserowFormulaLexer.OPEN_PAREN) { - searchingForIntegerLiteral = true - } else { - return formula - } - } else if (searchingForCloseParen) { - searchingForCloseParen = false - if (token.type !== BaserowFormulaLexer.CLOSE_PAREN) { - return formula - } - } else if (token.type === BaserowFormulaLexer.FIELDBYID) { - const futureIntLiteral = _lookaheadAndFindFieldByIdIntLiteral( - i + 1, - stream - ) - if ( - futureIntLiteral !== false && - fieldIdToName[futureIntLiteral] !== undefined - ) { - // Only replace field_by_id with field if we know we will find a proper int - // referenced we know about. Otherwise we want to leave the field_by_id - // untouched. - searchingForOpenParen = true - output = 'field' - } - } - } - - if (token.type === BaserowFormulaLexer.EOF) { - break - } - - newFormula += output - } - return newFormula -} - -function _lookaheadAndFindFieldByIdIntLiteral(start, stream) { - let searchingForIntegerLiteral = false - for (let i = start; i < stream.tokens.length; i++) { - const token = stream.tokens[i] - const isNormalToken = token.channel === 0 - - if (isNormalToken) { - if (searchingForIntegerLiteral) { - if (token.type === BaserowFormulaLexer.INTEGER_LITERAL) { - return parseInt(token.text) - } else { - return false - } - } else if (token.type === BaserowFormulaLexer.OPEN_PAREN) { - searchingForIntegerLiteral = true - } else { - return false - } - } - if (token.type === BaserowFormulaLexer.EOF) { - return false - } - } - return false -} diff --git a/web-frontend/modules/database/formula/parser/updateFieldNames.js b/web-frontend/modules/database/formula/parser/updateFieldNames.js deleted file mode 100644 index fd9fe5dbd..000000000 --- a/web-frontend/modules/database/formula/parser/updateFieldNames.js +++ /dev/null @@ -1,99 +0,0 @@ -import { BaserowFormulaLexer } from '@baserow/modules/database/formula/parser/generated/BaserowFormulaLexer' -import { getTokenStreamForFormula } from '@baserow/modules/database/formula/parser/parser' - -/** - * Given a map of old field name to new field name replaces all field references to - * old field names with their new names. Does so whist preserving any whitespace or - * comments. Any field references to names not in the map will be left as they are. - * - * @param formula The raw string to tokenize and transform. - * @param oldFieldNameToNewFieldName The map of old name to new name. - * @returns string The updated formula or if any invalid syntax was - * found the original string provided will be returned. - */ -export function updateFieldNames(formula, oldFieldNameToNewFieldName) { - const stream = getTokenStreamForFormula(formula) - - let searchingForOpenParen = false - let searchingForInnerFieldReferenceStringLiteral = false - let searchingForCloseParen = false - let newFormula = '' - - for (let i = 0; i < stream.tokens.length; i++) { - const token = stream.tokens[i] - let output = token.text - - // Whitespace and comments are on the hidden token channel. We ignore them entirely - // but still output them to ensure the user doesn't loose formatting or comments - // due to this update. - const isNormalToken = token.channel === 0 - - if (isNormalToken) { - if (searchingForInnerFieldReferenceStringLiteral) { - searchingForCloseParen = true - searchingForInnerFieldReferenceStringLiteral = false - if (token.type === BaserowFormulaLexer.SINGLEQ_STRING_LITERAL) { - output = _replaceFieldNameInStringLiteralIfMapped( - output, - "'", - oldFieldNameToNewFieldName - ) - } else if (token.type === BaserowFormulaLexer.DOUBLEQ_STRING_LITERAL) { - output = _replaceFieldNameInStringLiteralIfMapped( - output, - '"', - oldFieldNameToNewFieldName - ) - } else { - // The only valid normal token is a string literal, we've encountered a - // different token and hence the input string is invalid and so we'll just - // return it untouched. - return formula - } - } else if (searchingForOpenParen) { - searchingForOpenParen = false - if (token.type === BaserowFormulaLexer.OPEN_PAREN) { - searchingForInnerFieldReferenceStringLiteral = true - } else { - // The only valid normal token is a (, we've encountered a different - // token and hence the input string is invalid and so we'll just return it - // untouched. - return formula - } - } else if (searchingForCloseParen) { - searchingForCloseParen = false - if (token.type !== BaserowFormulaLexer.CLOSE_PAREN) { - // The only valid normal token is a ), we've encountered a different - // token and hence the input string is invalid and so we'll just return it - // untouched. - return formula - } - } else if (token.type === BaserowFormulaLexer.FIELD) { - searchingForOpenParen = true - } - } - if (token.type === BaserowFormulaLexer.EOF) { - break - } - newFormula += output - } - return newFormula -} - -function _replaceFieldNameInStringLiteralIfMapped( - fieldRefStringLiteral, - quote, - oldFieldNameToNewFieldName -) { - const unescapedOldName = fieldRefStringLiteral - .replace('\\' + quote, quote) - .slice(1, -1) - - const newName = oldFieldNameToNewFieldName[unescapedOldName] - if (newName !== undefined) { - const escapedNewName = newName.replace(quote, '\\' + quote) - return quote + escapedNewName + quote - } else { - return fieldRefStringLiteral - } -} diff --git a/web-frontend/modules/database/realtime.js b/web-frontend/modules/database/realtime.js index 786fd789c..c07708850 100644 --- a/web-frontend/modules/database/realtime.js +++ b/web-frontend/modules/database/realtime.js @@ -55,7 +55,7 @@ export const registerRealtimeEvents = (realtime) => { await store.dispatch('field/forceCreate', { table, values: data.field, - related_fields: relatedFields, + relatedFields, }) } if ( diff --git a/web-frontend/test/unit/database/formula/replaceFieldByIdWithField.spec.js b/web-frontend/test/unit/database/formula/replaceFieldByIdWithField.spec.js deleted file mode 100644 index a2558217a..000000000 --- a/web-frontend/test/unit/database/formula/replaceFieldByIdWithField.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import { replaceFieldByIdWithField } from '@baserow/modules/database/formula/parser/replaceFieldByIdWithField' - -function _assertReturnsSame(formula) { - const newFormula = replaceFieldByIdWithField(formula, { - 22: 'newName', - }) - expect(newFormula).toStrictEqual(formula) -} - -describe('Tests checking the replaceFieldByIdWithField formula parsing function', () => { - test('can replace a single quoted field by id', () => { - const newFormula = replaceFieldByIdWithField('field_by_id(1)', { - 1: 'newName', - }) - expect(newFormula).toStrictEqual("field('newName')") - }) - test('can replace a field by id reference keeping whitespace', () => { - const newFormula = replaceFieldByIdWithField('field_by_id( \n \n1 )', { - 1: 'newName', - }) - expect(newFormula).toStrictEqual("field( \n \n'newName' )") - }) - test('can replace a field by id with a name containing single quotes', () => { - const newFormula = replaceFieldByIdWithField('field_by_id(1)', { - 1: "newName with '", - }) - expect(newFormula).toStrictEqual("field('newName with \\'')") - }) - test('can replace a field by id with a name containing double quotes', () => { - const newFormula = replaceFieldByIdWithField('field_by_id(1)', { - 1: 'newName with "', - }) - expect(newFormula).toStrictEqual("field('newName with \"')") - }) - test('can replace a field by id keeping whitespace and comments', () => { - const newFormula = replaceFieldByIdWithField( - '/* comment */field_by_id(/* comment */ \n \n1 /* a comment */)', - { - 1: 'newName', - } - ) - expect(newFormula).toStrictEqual( - "/* comment */field(/* comment */ \n \n'newName' /* a comment */)" - ) - }) - test('can replace multiple different field by ids ', () => { - const newFormula = replaceFieldByIdWithField( - 'concat(field_by_id(1), field_by_id(1), field_by_id(2))', - { - 1: 'newName', - 2: 'newOther', - } - ) - expect(newFormula).toStrictEqual( - "concat(field('newName'), field('newName'), field('newOther'))" - ) - }) - test('doesnt change field by id not in dict', () => { - const newFormula = replaceFieldByIdWithField('field_by_id(2)', { - 1: 'newName', - }) - expect(newFormula).toStrictEqual('field_by_id(2)') - }) - test('returns same formula for invalid syntax', () => { - _assertReturnsSame('field_by_id(2') - _assertReturnsSame("field_by_id('test')") - _assertReturnsSame('field_by_id(test)') - _assertReturnsSame('field_by_id((test))') - _assertReturnsSame("field_by_id('''test'')") - _assertReturnsSame( - 'field_by_id(111111111111111111111111111111111111111111111111111111111111111)' - ) - }) -}) diff --git a/web-frontend/test/unit/database/formula/updateFieldNames.spec.js b/web-frontend/test/unit/database/formula/updateFieldNames.spec.js deleted file mode 100644 index c2d5b061b..000000000 --- a/web-frontend/test/unit/database/formula/updateFieldNames.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -import { updateFieldNames } from '@baserow/modules/database/formula/parser/updateFieldNames' - -function _assertReturnsSame(formula) { - const newFormula = updateFieldNames(formula, { - test: 'newName', - }) - expect(newFormula).toStrictEqual(formula) -} - -describe('Tests checking the updateFieldNames formula parsing function', () => { - test('can replace a single quoted field reference with one name', () => { - const newFormula = updateFieldNames("field('test')", { - test: 'newName', - }) - expect(newFormula).toStrictEqual("field('newName')") - }) - test('can replace a double quoted field reference with one name', () => { - const newFormula = updateFieldNames('field("test")', { - test: 'newName', - }) - expect(newFormula).toStrictEqual('field("newName")') - }) - test('can replace a field reference keeping whitespace', () => { - const newFormula = updateFieldNames('field( \n \n"test" )', { - test: 'newName', - }) - expect(newFormula).toStrictEqual('field( \n \n"newName" )') - }) - test('can replace a double quote field ref with a name containing single quotes', () => { - const newFormula = updateFieldNames('field("test")', { - test: "newName with '", - }) - expect(newFormula).toStrictEqual('field("newName with \'")') - }) - test('can replace a double quote field ref with a name containing double quotes', () => { - const newFormula = updateFieldNames('field("test")', { - test: 'newName with "', - }) - expect(newFormula).toStrictEqual('field("newName with \\"")') - }) - test('can replace a single quote field ref with a name containing single quotes', () => { - const newFormula = updateFieldNames("field('test')", { - test: "newName with '", - }) - expect(newFormula).toStrictEqual("field('newName with \\'')") - }) - test('can replace a single quote field ref with a name containing double quotes', () => { - const newFormula = updateFieldNames("field('test')", { - test: 'newName with "', - }) - expect(newFormula).toStrictEqual("field('newName with \"')") - }) - test('can replace a field reference keeping whitespace and comments', () => { - const newFormula = updateFieldNames( - '/* comment */field(/* comment */ \n \n"test" /* a comment */)', - { - test: 'newName', - } - ) - expect(newFormula).toStrictEqual( - '/* comment */field(/* comment */ \n \n"newName" /* a comment */)' - ) - }) - test('can replace multiple different field references ', () => { - const newFormula = updateFieldNames( - 'concat(field("test"), field("test"), field(\'other\'))', - { - test: 'newName', - other: 'newOther', - } - ) - expect(newFormula).toStrictEqual( - 'concat(field("newName"), field("newName"), field(\'newOther\'))' - ) - }) - test('doesnt change field names not in dict', () => { - const newFormula = updateFieldNames('field("test")', { - notTest: 'newName', - }) - expect(newFormula).toStrictEqual('field("test")') - }) - test('returns same formula for invalid syntax', () => { - _assertReturnsSame('field("test') - _assertReturnsSame('field(1)') - _assertReturnsSame('field(""""test"""")') - _assertReturnsSame('field(test)') - _assertReturnsSame('fielddsadsa(test)') - }) -})