Skip to content

Commit

Permalink
Lexing context tuning.
Browse files Browse the repository at this point in the history
Better class hierarchy. Related to #4740.
  • Loading branch information
pekkaklarck committed May 3, 2023
1 parent 23c0573 commit 3a5f5a0
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/robot/conf/languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Languages:
print(lang.name, lang.code)
"""

def __init__(self, languages: 'Iterable[LanguageLike]|LanguageLike' = (),
def __init__(self, languages: 'Iterable[LanguageLike]|LanguageLike|None' = (),
add_english: bool = True):
"""
:param languages: Initial language or list of languages.
Expand Down
44 changes: 22 additions & 22 deletions src/robot/parsing/lexer/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,38 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import cast

from robot.conf import Languages, LanguageLike, LanguagesLike
from robot.parsing.lexer.settings import Settings
from robot.utils import normalize_whitespace

from .settings import (InitFileSettings, Settings, SuiteFileSettings,
from .settings import (InitFileSettings, FileSettings, Settings, SuiteFileSettings,
ResourceFileSettings, TestCaseSettings, KeywordSettings)
from .tokens import Token


# TODO: Try making generic.
# TODO: Add separate __init__s for FileContext (accepts only lang) and Test/KwContext (accepts only settings)
class LexingContext:
settings_class: 'type[Settings]'

def __init__(self, settings: 'Settings|None' = None, lang: LanguagesLike = None):
if settings is None:
if not isinstance(lang, Languages):
lang = Languages(cast(LanguageLike, lang))
self.languages = lang
self.settings = self.settings_class(self.languages)
else:
self.languages = settings.languages
self.settings = settings

def __init__(self, settings: Settings, languages: Languages):
self.settings = settings
self.languages = languages

def lex_setting(self, statement: 'list[Token]'):
self.settings.lex(statement)


class FileContext(LexingContext):
settings: FileSettings

def __init__(self, lang: LanguagesLike = None):
languages = lang if isinstance(lang, Languages) else Languages(lang)
settings_class: 'type[FileSettings]' = type(self).__annotations__['settings']
settings = settings_class(languages)
super().__init__(settings, languages)

def add_language(self, lang: LanguageLike):
self.languages.add_language(lang)

def keyword_context(self) -> 'KeywordContext':
return KeywordContext(settings=KeywordSettings(self.languages))
return KeywordContext(KeywordSettings(self.settings))

def setting_section(self, statement: 'list[Token]') -> bool:
return self._handles_section(statement, 'Settings')
Expand Down Expand Up @@ -89,11 +84,10 @@ def _normalize(self, marker: str) -> str:


class SuiteFileContext(FileContext):
settings_class = SuiteFileSettings
settings: SuiteFileSettings

def test_case_context(self) -> 'TestCaseContext':
return TestCaseContext(settings=TestCaseSettings(self.settings, self.languages))
return TestCaseContext(TestCaseSettings(self.settings))

def test_case_section(self, statement: 'list[Token]') -> bool:
return self._handles_section(statement, 'Test Cases')
Expand All @@ -108,7 +102,7 @@ def _get_invalid_section_error(self, header: str) -> str:


class ResourceFileContext(FileContext):
settings_class = ResourceFileSettings
settings: ResourceFileSettings

def _get_invalid_section_error(self, header: str) -> str:
name = self._normalize(header)
Expand All @@ -119,7 +113,7 @@ def _get_invalid_section_error(self, header: str) -> str:


class InitFileContext(FileContext):
settings_class = InitFileSettings
settings: InitFileSettings

def _get_invalid_section_error(self, header: str) -> str:
name = self._normalize(header)
Expand All @@ -132,6 +126,9 @@ def _get_invalid_section_error(self, header: str) -> str:
class TestCaseContext(LexingContext):
settings: TestCaseSettings

def __init__(self, settings: TestCaseSettings):
super().__init__(settings, settings.languages)

@property
def template_set(self) -> bool:
return self.settings.template_set
Expand All @@ -140,6 +137,9 @@ def template_set(self) -> bool:
class KeywordContext(LexingContext):
settings: KeywordSettings

def __init__(self, settings: KeywordSettings):
super().__init__(settings, settings.languages)

@property
def template_set(self) -> bool:
return False
27 changes: 18 additions & 9 deletions src/robot/parsing/lexer/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,18 @@ def _validate(self, orig: str, name: str, statement: 'list[Token]'):
f"got {len(statement)-1}.")

def _get_non_existing_setting_message(self, name: str, normalized: str) -> str:
if self._is_valid_somewhere(normalized):
if self._is_valid_somewhere(normalized, Settings.__subclasses__()):
return self._not_valid_here(name)
return RecommendationFinder(normalize).find_and_format(
name=normalized,
candidates=tuple(self.settings) + tuple(self.aliases),
message=f"Non-existing setting '{name}'."
)

def _is_valid_somewhere(self, normalized: str) -> bool:
for cls in Settings.__subclasses__():
if normalized in cls.names or normalized in cls.aliases:
def _is_valid_somewhere(self, name: str, classes: 'list[type[Settings]]') -> bool:
for cls in classes:
if (name in cls.names or name in cls.aliases
or self._is_valid_somewhere(name, cls.__subclasses__())):
return True
return False

Expand Down Expand Up @@ -140,7 +141,11 @@ def _lex_arguments(self, tokens: 'list[Token]'):
token.type = Token.ARGUMENT


class SuiteFileSettings(Settings):
class FileSettings(Settings, ABC):
pass


class SuiteFileSettings(FileSettings):
names = (
'Documentation',
'Metadata',
Expand Down Expand Up @@ -171,7 +176,7 @@ def _not_valid_here(self, name: str) -> str:
return f"Setting '{name}' is not allowed in suite file."


class InitFileSettings(Settings):
class InitFileSettings(FileSettings):
names = (
'Documentation',
'Metadata',
Expand Down Expand Up @@ -199,7 +204,7 @@ def _not_valid_here(self, name: str) -> str:
return f"Setting '{name}' is not allowed in suite initialization file."


class ResourceFileSettings(Settings):
class ResourceFileSettings(FileSettings):
names = (
'Documentation',
'Keyword Tags',
Expand All @@ -222,8 +227,8 @@ class TestCaseSettings(Settings):
'Timeout'
)

def __init__(self, parent: SuiteFileSettings, languages: Languages):
super().__init__(languages)
def __init__(self, parent: SuiteFileSettings):
super().__init__(parent.languages)
self.parent = parent

def _format_name(self, name: str) -> str:
Expand Down Expand Up @@ -259,6 +264,10 @@ class KeywordSettings(Settings):
'Return'
)

def __init__(self, parent: FileSettings):
super().__init__(parent.languages)
self.parent = parent

def _format_name(self, name: str) -> str:
return name[1:-1].strip()

Expand Down

0 comments on commit 3a5f5a0

Please sign in to comment.