Working on #48 : Refactored FunctionParser, introducting ExpressionParser
This commit is contained in:
+7
-2
@@ -545,16 +545,21 @@ def decode_concept(text, wrapper="C"):
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def tokens_index(tokens, sub_tokens, skip=0):
|
def tokens_index(tokens, sub_tokens, skip=0, start_from_end=False):
|
||||||
"""
|
"""
|
||||||
Index of the sub tokens in tokens
|
Index of the sub tokens in tokens
|
||||||
:param tokens: tokens
|
:param tokens: tokens
|
||||||
:param sub_tokens: sub tokens to search
|
:param sub_tokens: sub tokens to search
|
||||||
:param skip: number of found to skip
|
:param skip: number of found to skip
|
||||||
|
:param start_from_end: start by the end
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
expected = [token.value for token in sub_tokens if token.type != TokenKind.EOF]
|
expected = [token.value for token in sub_tokens if token.type != TokenKind.EOF]
|
||||||
for i in range(0, len(tokens) - len(expected) + 1):
|
indexes = range(0, len(tokens) - len(expected) + 1)
|
||||||
|
if start_from_end:
|
||||||
|
indexes = reversed(indexes)
|
||||||
|
|
||||||
|
for i in indexes:
|
||||||
for j in range(len(expected)):
|
for j in range(len(expected)):
|
||||||
if tokens[i + j].value != expected[j]:
|
if tokens[i + j].value != expected[j]:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -232,3 +232,9 @@ class BaseParser:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name(name):
|
def get_name(name):
|
||||||
return BaseParser.PREFIX + name
|
return BaseParser.PREFIX + name
|
||||||
|
|
||||||
|
|
||||||
|
class BaseExprParser(BaseParser):
|
||||||
|
|
||||||
|
def parse_input(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from parsers.BaseParser import BaseExprParser
|
||||||
|
from parsers.FunctionParser import FunctionParser
|
||||||
|
from parsers.LogicalOperatorParser import LogicalOperatorParser
|
||||||
|
from parsers.RelationalOperatorParser import RelationalOperatorParser
|
||||||
|
from parsers.expressions import ParenthesisNode, NameExprNode
|
||||||
|
|
||||||
|
|
||||||
|
class ExpressionParser(BaseExprParser):
|
||||||
|
"""
|
||||||
|
Parses xxx (== | > | < | >= | <= | != | in | not in) yyy
|
||||||
|
Nothing else
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAME = "Expression"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(ExpressionParser.NAME, 60, False, yield_eof=True)
|
||||||
|
self.logical_parser = LogicalOperatorParser()
|
||||||
|
self.relational_parser = RelationalOperatorParser()
|
||||||
|
self.function_parser = FunctionParser()
|
||||||
|
|
||||||
|
def parse(self, context, parser_input: ParserInput):
|
||||||
|
"""
|
||||||
|
:param context:
|
||||||
|
:param parser_input:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(parser_input, ParserInput):
|
||||||
|
return None
|
||||||
|
|
||||||
|
context.log(f"Parsing '{parser_input}' with {self.NAME}Parser", self.name)
|
||||||
|
sheerka = context.sheerka
|
||||||
|
|
||||||
|
if parser_input.is_empty():
|
||||||
|
return context.sheerka.ret(self.name,
|
||||||
|
False,
|
||||||
|
sheerka.new(BuiltinConcepts.IS_EMPTY))
|
||||||
|
|
||||||
|
if not self.reset_parser(context, parser_input):
|
||||||
|
return self.sheerka.ret(
|
||||||
|
self.name,
|
||||||
|
False,
|
||||||
|
context.sheerka.new(BuiltinConcepts.ERROR, body=self.error_sink))
|
||||||
|
|
||||||
|
self.parser_input.next_token()
|
||||||
|
|
||||||
|
node = self.parse_input()
|
||||||
|
if isinstance(node, ParenthesisNode):
|
||||||
|
node = node.node
|
||||||
|
|
||||||
|
value = self.get_return_value_body(context.sheerka, self.parser_input.as_text(), node, node)
|
||||||
|
|
||||||
|
ret = self.sheerka.ret(
|
||||||
|
self.name,
|
||||||
|
not self.has_error,
|
||||||
|
value)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def parse_input(self):
|
||||||
|
pos = self.parser_input.pos
|
||||||
|
for parser in []: # [self.logical_parser, self.relational_parser, self.function_parser]:
|
||||||
|
self.parser_input.seek(pos) # reset position
|
||||||
|
if parser.reset_parser(self.context, self.parser_input):
|
||||||
|
res = parser.parse_input()
|
||||||
|
if res and not parser.has_error:
|
||||||
|
return res
|
||||||
|
|
||||||
|
return NameExprNode(self.parser_input.start, self.parser_input.end, self.parser_input.as_tokens())
|
||||||
@@ -6,14 +6,14 @@ from core.concept import Concept
|
|||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import TokenKind
|
from core.tokenizer import TokenKind
|
||||||
from core.utils import get_n_clones
|
from core.utils import get_n_clones
|
||||||
from parsers.BaseNodeParser import SourceCodeNode, SourceCodeWithConceptNode, UnrecognizedTokensNode
|
from parsers.BaseNodeParser import SourceCodeNode, SourceCodeWithConceptNode
|
||||||
from parsers.BaseParser import BaseParser, UnexpectedTokenParsingError, UnexpectedEofParsingError, Node
|
from parsers.BaseParser import UnexpectedTokenParsingError, UnexpectedEofParsingError, Node, BaseExprParser
|
||||||
from parsers.BnfNodeParser import BnfNodeParser
|
from parsers.BnfNodeParser import BnfNodeParser
|
||||||
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
|
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
|
||||||
from parsers.RuleParser import RuleParser
|
from parsers.RuleParser import RuleParser
|
||||||
from parsers.SequenceNodeParser import SequenceNodeParser
|
from parsers.SequenceNodeParser import SequenceNodeParser
|
||||||
from parsers.SyaNodeParser import SyaNodeParser
|
from parsers.SyaNodeParser import SyaNodeParser
|
||||||
from parsers.expressions import NameExprNode
|
from parsers.expressions import NameExprNode, FunctionNode, FunctionParameter
|
||||||
|
|
||||||
PARSERS = [RuleParser.NAME,
|
PARSERS = [RuleParser.NAME,
|
||||||
SequenceNodeParser.NAME,
|
SequenceNodeParser.NAME,
|
||||||
@@ -26,34 +26,7 @@ class FunctionParserNode(Node):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
class FunctionParser(BaseExprParser):
|
||||||
class FunctionParameter:
|
|
||||||
"""
|
|
||||||
class the represent result of the parameter parsing
|
|
||||||
"""
|
|
||||||
value: NameExprNode # value parsed
|
|
||||||
separator: NameExprNode = None # holds the value and the position of the separator
|
|
||||||
|
|
||||||
def add_sep(self, start, end, tokens):
|
|
||||||
self.separator = NameExprNode(start, end, tokens)
|
|
||||||
|
|
||||||
def value_to_unrecognized(self):
|
|
||||||
return UnrecognizedTokensNode(self.value.start, self.value.end, self.value.tokens).fix_source()
|
|
||||||
|
|
||||||
def separator_to_unrecognized(self):
|
|
||||||
if self.separator is None:
|
|
||||||
return None
|
|
||||||
return UnrecognizedTokensNode(self.separator.start, self.separator.end, self.separator.tokens).fix_source()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FunctionNode(FunctionParserNode):
|
|
||||||
first: NameExprNode # beginning of the function (it should represent the name of the function)
|
|
||||||
last: NameExprNode # last part of the function (it should be the trailing parenthesis)
|
|
||||||
parameters: list
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionParser(BaseParser):
|
|
||||||
"""
|
"""
|
||||||
The parser will be used to parse func(x, y, z)
|
The parser will be used to parse func(x, y, z)
|
||||||
where x, y and z can be source code, concepts or other functions
|
where x, y and z can be source code, concepts or other functions
|
||||||
@@ -144,6 +117,9 @@ class FunctionParser(BaseParser):
|
|||||||
|
|
||||||
return res[0] if len(res) == 1 else res
|
return res[0] if len(res) == 1 else res
|
||||||
|
|
||||||
|
def parse_input(self):
|
||||||
|
return self.parse_function()
|
||||||
|
|
||||||
def parse_function(self):
|
def parse_function(self):
|
||||||
|
|
||||||
start = self.parser_input.pos
|
start = self.parser_input.pos
|
||||||
@@ -168,20 +144,23 @@ class FunctionParser(BaseParser):
|
|||||||
start_node = NameExprNode(start, start + 1, self.parser_input.tokens[start:start + 2])
|
start_node = NameExprNode(start, start + 1, self.parser_input.tokens[start:start + 2])
|
||||||
if not self.parser_input.next_token():
|
if not self.parser_input.next_token():
|
||||||
self.add_error(UnexpectedEofParsingError(f"Unexpected EOF after left parenthesis"))
|
self.add_error(UnexpectedEofParsingError(f"Unexpected EOF after left parenthesis"))
|
||||||
return FunctionNode(start_node, None, None)
|
return FunctionNode(start, start + 1, [], start_node, None, None)
|
||||||
|
|
||||||
params = self.parse_parameters()
|
params = self.parse_parameters()
|
||||||
if self.has_error:
|
if self.has_error:
|
||||||
return FunctionNode(start_node, None, params)
|
return FunctionNode(start, self.parser_input.pos, [], start_node, None, params)
|
||||||
|
|
||||||
token = self.parser_input.token
|
token = self.parser_input.token
|
||||||
if not token or token.type != TokenKind.RPAR:
|
if not token or token.type != TokenKind.RPAR:
|
||||||
self.add_error(UnexpectedTokenParsingError(f"Right parenthesis not found",
|
self.add_error(UnexpectedTokenParsingError(f"Right parenthesis not found",
|
||||||
token,
|
token,
|
||||||
[TokenKind.RPAR]))
|
[TokenKind.RPAR]))
|
||||||
return FunctionNode(start_node, None, params)
|
return FunctionNode(start, self.parser_input.pos, [], start_node, None, params)
|
||||||
|
|
||||||
return FunctionNode(start_node,
|
return FunctionNode(start,
|
||||||
|
self.parser_input.pos,
|
||||||
|
self.parser_input.tokens[start:self.parser_input.pos + 1],
|
||||||
|
start_node,
|
||||||
NameExprNode(self.parser_input.pos, self.parser_input.pos, [token]),
|
NameExprNode(self.parser_input.pos, self.parser_input.pos, [token]),
|
||||||
params)
|
params)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from core.sheerka.services.sheerka_service import FailedToCompileError
|
|||||||
from core.tokenizer import TokenKind, Tokenizer, Keywords
|
from core.tokenizer import TokenKind, Tokenizer, Keywords
|
||||||
from core.utils import get_text_from_tokens
|
from core.utils import get_text_from_tokens
|
||||||
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
from parsers.BaseNodeParser import UnrecognizedTokensNode
|
||||||
from parsers.BaseParser import BaseParser, UnexpectedTokenParsingError, UnexpectedEofParsingError
|
from parsers.BaseParser import BaseParser, UnexpectedTokenParsingError, UnexpectedEofParsingError, BaseExprParser
|
||||||
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
|
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
|
||||||
from parsers.expressions import ParenthesisNode, OrNode, AndNode, NotNode, LeftPartNotFoundError, \
|
from parsers.expressions import ParenthesisNode, OrNode, AndNode, NotNode, LeftPartNotFoundError, \
|
||||||
ParenthesisMismatchError, NameExprNode, ExprNode, VariableNode, ComparisonNode
|
ParenthesisMismatchError, NameExprNode, ExprNode, VariableNode, ComparisonNode
|
||||||
@@ -60,7 +60,7 @@ class ReteConditionsEmitter:
|
|||||||
return [AndConditions(conditions)]
|
return [AndConditions(conditions)]
|
||||||
|
|
||||||
|
|
||||||
class LogicalOperatorParser(BaseParser):
|
class LogicalOperatorParser(BaseExprParser):
|
||||||
"""
|
"""
|
||||||
will parser logic expression
|
will parser logic expression
|
||||||
like not (a and b or c)
|
like not (a and b or c)
|
||||||
@@ -93,7 +93,7 @@ class LogicalOperatorParser(BaseParser):
|
|||||||
if not isinstance(parser_input, ParserInput):
|
if not isinstance(parser_input, ParserInput):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
context.log(f"Parsing '{parser_input}' with LogicalOperatorParser", self.name)
|
context.log(f"Parsing '{parser_input}' with {self.NAME}Parser", self.name)
|
||||||
sheerka = context.sheerka
|
sheerka = context.sheerka
|
||||||
|
|
||||||
if parser_input.is_empty():
|
if parser_input.is_empty():
|
||||||
@@ -108,7 +108,7 @@ class LogicalOperatorParser(BaseParser):
|
|||||||
context.sheerka.new(BuiltinConcepts.ERROR, body=self.error_sink))
|
context.sheerka.new(BuiltinConcepts.ERROR, body=self.error_sink))
|
||||||
|
|
||||||
self.parser_input.next_token()
|
self.parser_input.next_token()
|
||||||
tree = self.parse_or()
|
tree = self.parse_input()
|
||||||
token = self.parser_input.token
|
token = self.parser_input.token
|
||||||
if token and token.type != TokenKind.EOF:
|
if token and token.type != TokenKind.EOF:
|
||||||
self.add_error(UnexpectedTokenParsingError(f"Unexpected token '{token}'", token, []))
|
self.add_error(UnexpectedTokenParsingError(f"Unexpected token '{token}'", token, []))
|
||||||
@@ -124,6 +124,9 @@ class LogicalOperatorParser(BaseParser):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def parse_input(self):
|
||||||
|
return self.parse_or()
|
||||||
|
|
||||||
def parse_or(self):
|
def parse_or(self):
|
||||||
start = self.parser_input.pos
|
start = self.parser_input.pos
|
||||||
expr = self.parse_and()
|
expr = self.parse_and()
|
||||||
|
|||||||
@@ -4,19 +4,21 @@ from core.builtin_concepts_ids import BuiltinConcepts
|
|||||||
from core.sheerka.services.SheerkaExecute import ParserInput, SheerkaExecute
|
from core.sheerka.services.SheerkaExecute import ParserInput, SheerkaExecute
|
||||||
from core.tokenizer import TokenKind, Token
|
from core.tokenizer import TokenKind, Token
|
||||||
from core.utils import get_text_from_tokens
|
from core.utils import get_text_from_tokens
|
||||||
from parsers.BaseParser import BaseParser, UnexpectedTokenParsingError
|
from parsers.BaseParser import UnexpectedTokenParsingError, BaseExprParser
|
||||||
from parsers.expressions import ComparisonNode, ParenthesisMismatchError, NameExprNode, ComparisonType, VariableNode, \
|
from parsers.expressions import ComparisonNode, ParenthesisMismatchError, NameExprNode, ComparisonType, VariableNode, \
|
||||||
ParenthesisNode, LeftPartNotFoundError
|
ParenthesisNode, LeftPartNotFoundError
|
||||||
|
|
||||||
|
|
||||||
class RelationalOperatorParser(BaseParser):
|
class RelationalOperatorParser(BaseExprParser):
|
||||||
"""
|
"""
|
||||||
Parses xxx (== | > | < | >= | <= | != | in | not in) yyy
|
Parses xxx (== | > | < | >= | <= | != | in | not in) yyy
|
||||||
Nothing else
|
Nothing else
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
NAME = "RelationalOperator"
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__("Expression", 60, False, yield_eof=True)
|
super().__init__(self.NAME, 60, False, yield_eof=True)
|
||||||
|
|
||||||
def parse(self, context, parser_input: Union[ParserInput, List[Token]]):
|
def parse(self, context, parser_input: Union[ParserInput, List[Token]]):
|
||||||
"""
|
"""
|
||||||
@@ -31,7 +33,7 @@ class RelationalOperatorParser(BaseParser):
|
|||||||
elif not isinstance(parser_input, ParserInput):
|
elif not isinstance(parser_input, ParserInput):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
context.log(f"Parsing '{parser_input}' with ComparisonExpressionParser", self.name)
|
context.log(f"Parsing '{parser_input}' with {self.NAME}Parser", self.name)
|
||||||
sheerka = context.sheerka
|
sheerka = context.sheerka
|
||||||
|
|
||||||
if parser_input.is_empty():
|
if parser_input.is_empty():
|
||||||
@@ -47,7 +49,7 @@ class RelationalOperatorParser(BaseParser):
|
|||||||
|
|
||||||
self.parser_input.next_token()
|
self.parser_input.next_token()
|
||||||
|
|
||||||
node = self.parse_compare()
|
node = self.parse_input()
|
||||||
if isinstance(node, ParenthesisNode):
|
if isinstance(node, ParenthesisNode):
|
||||||
node = node.node
|
node = node.node
|
||||||
|
|
||||||
@@ -60,6 +62,9 @@ class RelationalOperatorParser(BaseParser):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def parse_input(self):
|
||||||
|
return self.parse_compare()
|
||||||
|
|
||||||
def parse_compare(self):
|
def parse_compare(self):
|
||||||
start = self.parser_input.pos
|
start = self.parser_input.pos
|
||||||
left = self.parse_names()
|
left = self.parse_names()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple, Union
|
||||||
|
|
||||||
from core.tokenizer import Token, TokenKind, Tokenizer
|
from core.tokenizer import Token, TokenKind, Tokenizer
|
||||||
from core.utils import tokens_are_matching
|
from core.utils import tokens_are_matching
|
||||||
@@ -63,7 +63,6 @@ class ExprNode(Node):
|
|||||||
class NameExprNode(ExprNode):
|
class NameExprNode(ExprNode):
|
||||||
def __init__(self, start, end, tokens):
|
def __init__(self, start, end, tokens):
|
||||||
super().__init__(start, end, tokens)
|
super().__init__(start, end, tokens)
|
||||||
self.tokens = tokens
|
|
||||||
self.value = "".join([t.str_value for t in self.tokens])
|
self.value = "".join([t.str_value for t in self.tokens])
|
||||||
|
|
||||||
def eval(self, obj):
|
def eval(self, obj):
|
||||||
@@ -224,8 +223,8 @@ class ParenthesisNode(ExprNode):
|
|||||||
if self.start != other.start or self.end != other.end:
|
if self.start != other.start or self.end != other.end:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# if other.tokens is not None and other.tokens != self.tokens:
|
if other.tokens is not None and other.tokens != self.tokens:
|
||||||
# return False
|
return False
|
||||||
|
|
||||||
return self.node == other.node
|
return self.node == other.node
|
||||||
|
|
||||||
@@ -302,6 +301,33 @@ class ComparisonNode(ExprNode):
|
|||||||
return f"{self.left} {self.comp} {self.right}"
|
return f"{self.left} {self.comp} {self.right}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class FunctionParameter:
|
||||||
|
"""
|
||||||
|
class the represent result of the parameter parsing
|
||||||
|
"""
|
||||||
|
value: NameExprNode # value parsed
|
||||||
|
separator: NameExprNode = None # holds the value and the position of the separator
|
||||||
|
|
||||||
|
def add_sep(self, start, end, tokens):
|
||||||
|
self.separator = NameExprNode(start, end, tokens)
|
||||||
|
|
||||||
|
def value_to_unrecognized(self):
|
||||||
|
return UnrecognizedTokensNode(self.value.start, self.value.end, self.value.tokens).fix_source()
|
||||||
|
|
||||||
|
def separator_to_unrecognized(self):
|
||||||
|
if self.separator is None:
|
||||||
|
return None
|
||||||
|
return UnrecognizedTokensNode(self.separator.start, self.separator.end, self.separator.tokens).fix_source()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionNode(ExprNode):
|
||||||
|
first: NameExprNode # beginning of the function (it should represent the name of the function)
|
||||||
|
last: NameExprNode # last part of the function (it should be the trailing parenthesis)
|
||||||
|
parameters: Union[None, List[FunctionParameter]]
|
||||||
|
|
||||||
|
|
||||||
class ExpressionVisitor:
|
class ExpressionVisitor:
|
||||||
"""
|
"""
|
||||||
Pyhtonic implementation of visitors for ExprNode
|
Pyhtonic implementation of visitors for ExprNode
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from parsers.FunctionParser import FunctionNode
|
|||||||
from parsers.PythonParser import PythonNode
|
from parsers.PythonParser import PythonNode
|
||||||
from parsers.SyaNodeParser import SyaConceptParserHelper
|
from parsers.SyaNodeParser import SyaConceptParserHelper
|
||||||
from parsers.expressions import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, ComparisonType, \
|
from parsers.expressions import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, ComparisonType, \
|
||||||
ParenthesisNode
|
FunctionParameter
|
||||||
from sheerkarete.common import V
|
from sheerkarete.common import V
|
||||||
from sheerkarete.conditions import Condition, AndConditions
|
from sheerkarete.conditions import Condition, AndConditions
|
||||||
|
|
||||||
@@ -972,8 +972,13 @@ def get_expr_node_from_test_node(full_text, test_node):
|
|||||||
return start, end
|
return start, end
|
||||||
|
|
||||||
def get_pos_from_source(source):
|
def get_pos_from_source(source):
|
||||||
|
if isinstance(source, tuple):
|
||||||
|
source, to_skip = source[0], source[1]
|
||||||
|
else:
|
||||||
|
to_skip = 0
|
||||||
|
|
||||||
source_as_node = list(Tokenizer(source, yield_eof=False))
|
source_as_node = list(Tokenizer(source, yield_eof=False))
|
||||||
start = tokens_index(full_text_as_tokens, source_as_node)
|
start = tokens_index(full_text_as_tokens, source_as_node, skip=to_skip)
|
||||||
end = start + len(source_as_node) - 1
|
end = start + len(source_as_node) - 1
|
||||||
return start, end
|
return start, end
|
||||||
|
|
||||||
@@ -1017,11 +1022,31 @@ def get_expr_node_from_test_node(full_text, test_node):
|
|||||||
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1],
|
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1],
|
||||||
node_type, left_node, right_node)
|
node_type, left_node, right_node)
|
||||||
|
|
||||||
if isinstance(node, PAREN):
|
if isinstance(node, FN):
|
||||||
value_as_tokens = list(Tokenizer(node.source, yield_eof=False))
|
start, end = get_pos_from_source(node.first)
|
||||||
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
|
first = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
end = start + len(value_as_tokens) - 1
|
start, end = get_pos_from_source(node.last)
|
||||||
return ParenthesisNode(start, end, value_as_tokens, get_expr_node(node.node))
|
last = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
parameters = []
|
||||||
|
for param_value, sep in node.parameters:
|
||||||
|
if isinstance(param_value, str):
|
||||||
|
start, end = get_pos_from_source(param_value)
|
||||||
|
param_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
else:
|
||||||
|
param_as_expr_node = get_expr_node(param_value)
|
||||||
|
|
||||||
|
if sep:
|
||||||
|
sep_tokens = Tokenizer(sep, yield_eof=False)
|
||||||
|
start = param_as_expr_node.end + 1
|
||||||
|
end = start + len(list(sep_tokens)) - 1
|
||||||
|
sep_as_expr_node = NameExprNode(start, end, full_text_as_tokens[start: end + 1])
|
||||||
|
else:
|
||||||
|
sep_as_expr_node = None
|
||||||
|
|
||||||
|
parameters.append(FunctionParameter(param_as_expr_node, sep_as_expr_node))
|
||||||
|
|
||||||
|
start, end = first.start, last.end
|
||||||
|
return FunctionNode(start, end, full_text_as_tokens[start: end + 1], first, last, parameters)
|
||||||
|
|
||||||
return get_expr_node(test_node)
|
return get_expr_node(test_node)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
|
from core.tokenizer import Tokenizer
|
||||||
|
from parsers.ExpressionParser import ExpressionParser
|
||||||
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR
|
||||||
|
|
||||||
|
|
||||||
|
class TestExpressionParser(TestUsingMemoryBasedSheerka):
|
||||||
|
def init_parser(self):
|
||||||
|
sheerka, context = self.init_concepts()
|
||||||
|
parser = ExpressionParser()
|
||||||
|
return sheerka, context, parser
|
||||||
|
|
||||||
|
def input_parser_with_source(self, source):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
parser.reset_parser(context, ParserInput(source))
|
||||||
|
return sheerka, context, parser
|
||||||
|
|
||||||
|
def test_i_can_detect_empty_expression(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
res = parser.parse(context, ParserInput(""))
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expression, expected", [
|
||||||
|
("something that i do not recognize", EXPR("something that i do not recognize")),
|
||||||
|
])
|
||||||
|
def test_i_can_parse_input(self, expression, expected):
|
||||||
|
sheerka, context, parser = self.input_parser_with_source(expression)
|
||||||
|
expected = get_expr_node_from_test_node(expression, expected)
|
||||||
|
|
||||||
|
parsed = parser.parse_input()
|
||||||
|
|
||||||
|
assert not parser.has_error
|
||||||
|
assert parsed == expected
|
||||||
|
|
||||||
|
def test_i_can_parse_sub_tokens(self):
|
||||||
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|
||||||
|
expression = "do not care var.attr do not care either"
|
||||||
|
parser_input = ParserInput("text", Tokenizer(expression, yield_eof=False), start=6, end=8)
|
||||||
|
|
||||||
|
parser.reset_parser(context, parser_input)
|
||||||
|
parsed = parser.parse_input()
|
||||||
|
|
||||||
|
assert not parser.has_error
|
||||||
|
assert parsed == get_expr_node_from_test_node(expression, [VAR("var.attr")])
|
||||||
@@ -6,7 +6,8 @@ from core.sheerka.services.SheerkaExecute import ParserInput
|
|||||||
from parsers.FunctionParser import FunctionParser
|
from parsers.FunctionParser import FunctionParser
|
||||||
from parsers.PythonParser import PythonErrorNode
|
from parsers.PythonParser import PythonErrorNode
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
from tests.parsers.parsers_utils import compute_expected_array, SCN, SCWC, CN, UTN, CNC, RN, FN, get_test_obj
|
from tests.parsers.parsers_utils import compute_expected_array, SCN, SCWC, CN, UTN, CNC, RN, FN, get_test_obj, \
|
||||||
|
get_expr_node_from_test_node
|
||||||
|
|
||||||
cmap = {
|
cmap = {
|
||||||
"one": Concept("one"),
|
"one": Concept("one"),
|
||||||
@@ -64,12 +65,12 @@ class TestFunctionParser(TestUsingMemoryBasedSheerka):
|
|||||||
("concept(one)", FN("concept(", ")", ["one"])),
|
("concept(one)", FN("concept(", ")", ["one"])),
|
||||||
("func(one)", FN("func(", ")", ["one"])),
|
("func(one)", FN("func(", ")", ["one"])),
|
||||||
("func(a long two, 'three', ;:$*)", FN("func(", ")", ["a long two, ", "'three', ", ";:$*"])),
|
("func(a long two, 'three', ;:$*)", FN("func(", ")", ["a long two, ", "'three', ", ";:$*"])),
|
||||||
("func(func1(one), two, func2(func3(), func4(three)))", FN("func(", ")", [
|
("func(func1(one), two, func2(func3(), func4(three)))", FN("func(", (")", 4), [
|
||||||
(FN("func1(", ")", ["one"]), ", "),
|
(FN("func1(", ")", ["one"]), ", "),
|
||||||
"two, ",
|
"two, ",
|
||||||
(FN("func2(", ")", [
|
(FN("func2(", (")", 3), [
|
||||||
(FN("func3(", ")", []), ", "),
|
(FN("func3(", (")", 1), []), ", "),
|
||||||
(FN("func4(", ")", ["three"]), None),
|
(FN("func4(", (")", 2), ["three"]), None),
|
||||||
]), None)
|
]), None)
|
||||||
])),
|
])),
|
||||||
])
|
])
|
||||||
@@ -80,8 +81,8 @@ class TestFunctionParser(TestUsingMemoryBasedSheerka):
|
|||||||
parser.parser_input.next_token()
|
parser.parser_input.next_token()
|
||||||
|
|
||||||
res = parser.parse_function()
|
res = parser.parse_function()
|
||||||
transformed_res = get_test_obj(res, expected)
|
expected = get_expr_node_from_test_node(expression, expected)
|
||||||
assert transformed_res == expected
|
assert res == expected
|
||||||
|
|
||||||
def test_i_can_parse_function_when_rule(self):
|
def test_i_can_parse_function_when_rule(self):
|
||||||
sheerka, context, parser = self.init_parser()
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import pytest
|
|||||||
from core.builtin_concepts_ids import BuiltinConcepts
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||||
from core.tokenizer import TokenKind, Tokenizer
|
from core.tokenizer import TokenKind, Tokenizer
|
||||||
|
from parsers.BaseParser import UnexpectedTokenParsingError
|
||||||
from parsers.RelationalOperatorParser import RelationalOperatorParser
|
from parsers.RelationalOperatorParser import RelationalOperatorParser
|
||||||
from parsers.expressions import ParenthesisMismatchError
|
from parsers.expressions import ParenthesisMismatchError
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
@@ -94,9 +95,7 @@ class TestRelationalOperatorParser(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
assert not res.status
|
assert not res.status
|
||||||
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
|
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
|
||||||
assert isinstance(res.body.body[0], UnexpectedTokenError)
|
assert isinstance(res.body.body[0], UnexpectedTokenParsingError)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_i_can_parse_tokens_rather_than_parser_input(self):
|
def test_i_can_parse_tokens_rather_than_parser_input(self):
|
||||||
sheerka, context, parser = self.init_parser()
|
sheerka, context, parser = self.init_parser()
|
||||||
|
|||||||
Reference in New Issue
Block a user