mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-04 21:25:24 +00:00
Deprecate field_by_id special baserow formula function to greatly simplify...
This commit is contained in:
parent
757148947a
commit
7df4e258be
59 changed files with 3207 additions and 2597 deletions
backend
src/baserow/contrib/database
api/formula
application_types.pyfields
formula
ast
expression_generator
parser
ast_mapper.pyexceptions.py
generated
BaserowFormula.interpBaserowFormula.pyBaserowFormula.tokensBaserowFormulaLexer.interpBaserowFormulaLexer.pyBaserowFormulaLexer.tokensBaserowFormulaListener.pyBaserowFormulaVisitor.py
parser.pyreplace_field_by_id_with_field.pyreplace_field_with_field_by_id.pyupdate_field_names.pytypes
migrations
table
trash
tests/baserow/contrib/database
docs/guides
formula
web-frontend
modules/database
components/field
formula/parser
generated
BaserowFormula.interpBaserowFormula.jsBaserowFormula.tokensBaserowFormulaLexer.interpBaserowFormulaLexer.jsBaserowFormulaLexer.tokensBaserowFormulaListener.jsBaserowFormulaVisitor.js
replaceFieldByIdWithField.jsupdateFieldNames.jstest/unit/database/formula
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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]"
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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]
|
||||
[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]
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
del BaserowFormula
|
||||
|
|
|
@ -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
|
||||
del BaserowFormula
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
]
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)",
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)"
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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]
|
||||
[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]
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ export const registerRealtimeEvents = (realtime) => {
|
|||
await store.dispatch('field/forceCreate', {
|
||||
table,
|
||||
values: data.field,
|
||||
related_fields: relatedFields,
|
||||
relatedFields,
|
||||
})
|
||||
}
|
||||
if (
|
||||
|
|
|
@ -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)'
|
||||
)
|
||||
})
|
||||
})
|
|
@ -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)')
|
||||
})
|
||||
})
|
Loading…
Add table
Reference in a new issue