Source code for devana.preprocessing.premade.components.parser.functionparser

from dataclasses import dataclass, field
from typing import List, Optional, Callable, NoReturn
from enum import Enum, auto


[docs]@dataclass class FunctionEntity: """Parsing function information, argument are provided as string for another parser.""" name: str namespaces: List[str] arguments: str
[docs]class FunctionParser: """Class realized parsing function."""
[docs] class StateName(Enum): """Name of state.""" SEARCH = auto() NAMESPACE = auto() NAMESPACE_SEPARATOR = auto() FUNCTION_SEPARATOR = auto() ARGS = auto() STRING = auto() STRING_ESCAPE = auto() END = auto() ERROR = auto()
@classmethod def _transactions_search(cls, character: Optional[str]) -> StateName: if character.isspace(): return cls.StateName.SEARCH if character.isalpha(): return cls.StateName.NAMESPACE return cls.StateName.ERROR @classmethod def _transactions_namespace(cls, character: Optional[str]) -> StateName: if character is None: return cls.StateName.END if character == ":": return cls.StateName.NAMESPACE_SEPARATOR if character == "(": return cls.StateName.ARGS if character.isalnum() or character == "_": return cls.StateName.NAMESPACE if character.isspace(): return cls.StateName.FUNCTION_SEPARATOR if character == ",": return cls.StateName.FUNCTION_SEPARATOR return cls.StateName.ERROR @classmethod def _transactions_namespace_separator(cls, character: Optional[str]) -> StateName: if character == ":": return cls.StateName.NAMESPACE return cls.StateName.ERROR @classmethod def _transactions_function_separator(cls, character: Optional[str]) -> StateName: if character is None: return cls.StateName.END if character == ",": return cls.StateName.SEARCH if character.isspace(): return cls.StateName.FUNCTION_SEPARATOR return cls.StateName.ERROR @classmethod def _transactions_args(cls, character: Optional[str]) -> StateName: if character is None: return cls.StateName.ERROR if character == ")": return cls.StateName.FUNCTION_SEPARATOR else: return cls.StateName.ARGS @classmethod def _transactions_string(cls, character: Optional[str]) -> StateName: if character == '"': return cls.StateName.ARGS if character == '\\': return cls.StateName.STRING_ESCAPE return cls.StateName.STRING @classmethod def _transactions_string_escape(cls, _: Optional[str]) -> StateName: return cls.StateName.STRING @classmethod def _transactions_error(cls, _: Optional[str]) -> StateName: return cls.StateName.END @classmethod def _transactions_end(cls, _: Optional[str]) -> StateName: return cls.StateName.END
[docs] @dataclass class State: """State information of parser. """ transactions: Callable[[Optional[str]], "FunctionParser.StateName"] on_enter: Callable[[Optional[str]], NoReturn] = field(default=lambda _: None) on_exit: Callable[[], NoReturn] = field(default=lambda: None) on_self: Callable[[Optional[str]], NoReturn] = field(default=lambda _: None)
def _accumulate(self, character: Optional[str]): self._current_name_string += character def _add_name(self): self._names.append(self._current_name_string) self._current_name_string = "" def _set_arguments(self, character: Optional[str]): self._arguments += character def _add_function(self): if not self._names: return self._functions.append(FunctionEntity(self._names[-1], self._names[:-1], self._arguments)) self._names = [] self._arguments = "" self._current_name_string = "" def _on_error(self, character: Optional[str]): raise ValueError("Unable to parse function.") def __init__(self): self._current_name_string = "" self._name = "" self._names = [] self._arguments = "" self._functions: List[FunctionEntity] = [] self._states = { self.StateName.SEARCH: self.State(self._transactions_search), self.StateName.NAMESPACE: self.State(self._transactions_namespace, on_self= self._accumulate, on_enter=lambda ch: self._accumulate(ch) if ch.isalpha() else None, on_exit=self._add_name), self.StateName.NAMESPACE_SEPARATOR: self.State(self._transactions_namespace_separator), self.StateName.FUNCTION_SEPARATOR: self.State(self._transactions_function_separator, on_exit=self._add_function), self.StateName.ARGS: self.State(self._transactions_args, on_self=self._set_arguments), self.StateName.STRING: self.State(self._transactions_string, on_self=self._accumulate, on_enter=self._accumulate, on_exit=self._add_function), self.StateName.STRING_ESCAPE: self.State(self._transactions_string_escape), self.StateName.END: self.State(self._transactions_end, on_exit=self._add_function), self.StateName.ERROR: self.State(self._transactions_error, on_enter=self._on_error) } def _process_state(self, character: Optional[str], next_state: StateName, current_state: StateName): if next_state == current_state: self._states[current_state].on_self(character) else: self._states[current_state].on_exit() self._states[next_state].on_enter(character)
[docs] def parse(self, text: str) -> List[FunctionEntity]: """Parse line to find functions.""" self._functions = [] self._current_name_string = "" self._arguments = "" self._names = [] current_state = self.StateName.SEARCH for character in text: next_state = self._states[current_state].transactions(character) self._process_state(character, next_state, current_state) current_state = next_state next_state = self._states[current_state].transactions(None) self._process_state(None, next_state, current_state) current_state = next_state self._states[current_state].on_exit() return self._functions