Skip to content

Commit

Permalink
Topologically sort specifications after parsing
Browse files Browse the repository at this point in the history
Closes #572
  • Loading branch information
Alexander Senier committed Mar 5, 2021
1 parent 6c47725 commit 6190139
Showing 1 changed file with 38 additions and 10 deletions.
48 changes: 38 additions & 10 deletions rflx/specification/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
from collections import OrderedDict
from pathlib import Path
from typing import Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Type, Union
from typing import Callable, Dict, List, Mapping, Optional, Sequence, Set, Tuple, Type, Union

from librflxlang import (
AnalysisContext,
Expand Down Expand Up @@ -1181,7 +1181,9 @@ def check_naming(error: RecordFluxError, package: PackageNode, name: Path) -> No
class Parser:
def __init__(self, skip_verification: bool = False, cached: bool = False) -> None:
self.skip_verification = skip_verification
self.__specifications: OrderedDict[str, Tuple[Path, Specification]] = OrderedDict()
self.__specifications: OrderedDict[
str, Tuple[Path, Specification, List[str]]
] = OrderedDict()
self.__types: List[model.Type] = [
*model.BUILTIN_TYPES.values(),
*model.INTERNAL_TYPES.values(),
Expand All @@ -1194,11 +1196,11 @@ def __convert_unit(
) -> RecordFluxError:
transitions = transitions or []
error = RecordFluxError()
withed_files = []

if spec:
check_naming(error, spec.f_package_declaration, filename)
packagefile = f"{spec.f_package_declaration.f_identifier.text.lower()}.rflx"
self.__specifications[packagefile] = (filename, spec)
for context in spec.f_context_clause:
item = create_id(context.f_item, filename)
if item in transitions:
Expand All @@ -1221,16 +1223,15 @@ def __convert_unit(
)
continue
withed_file = filename.parent / f"{str(item).lower()}.rflx"
error.extend(self.__parse_specfile(withed_file, transitions + [item]))
withed_files.append(withed_file.name)
if withed_file.name not in self.__specifications:
error.extend(self.__parse_specfile(withed_file, transitions + [item]))
self.__specifications[packagefile] = (filename, spec, withed_files)

return error

def __parse_specfile(self, filename: Path, transitions: List[ID] = None) -> RecordFluxError:
error = RecordFluxError()
if filename.name in self.__specifications:
self.__specifications.move_to_end(filename.name)
return error

transitions = transitions or []

log.info("Parsing %s", filename)
Expand All @@ -1239,11 +1240,37 @@ def __parse_specfile(self, filename: Path, transitions: List[ID] = None) -> Reco
return error
return self.__convert_unit(unit.root, filename, transitions)

def __sort_specs_topologically(self) -> None:
"""
(Reverse) Topologically sort specifications using Kahn's algorithm.
"""

result: List[str] = []
incoming: Dict[str, Set[str]] = {f: set() for f in self.__specifications.keys()}
for filename, (_, _, deps) in self.__specifications.items():
for d in deps:
if d in incoming:
incoming[d].add(filename)

specs = [f for f, i in incoming.items() if len(i) == 0]
visited = set(specs)

while specs:
s = specs.pop(0)
result.insert(0, s)
for e in self.__specifications[s][2]:
visited.add(e)
if e in incoming and incoming[e] <= visited:
specs.append(e)

self.__specifications = OrderedDict((f, self.__specifications[f]) for f in result)

def parse(self, *specfiles: Path) -> None:
error = RecordFluxError()

for f in specfiles:
error.extend(self.__parse_specfile(f))
self.__sort_specs_topologically()
error.propagate()

def parse_string(
Expand All @@ -1255,11 +1282,12 @@ def parse_string(
unit = AnalysisContext().get_from_buffer("<stdin>", string, rule=rule)
if not diagnostics_to_error(unit.diagnostics, error, STDIN):
error = self.__convert_unit(unit.root, STDIN)
self.__sort_specs_topologically()
error.propagate()

def create_model(self) -> model.Model:
error = RecordFluxError()
for filename, spec in reversed(self.__specifications.values()):
for filename, spec, _ in self.__specifications.values():
try:
self.__evaluate_specification(spec, filename)
except RecordFluxError as e:
Expand All @@ -1276,7 +1304,7 @@ def create_model(self) -> model.Model:
def specifications(self) -> Dict[str, Specification]:
return {
spec.f_package_declaration.f_identifier.text: spec
for _, spec in self.__specifications.values()
for _, spec, _ in self.__specifications.values()
}

def __evaluate_specification(self, spec: Specification, filename: Path) -> None:
Expand Down

0 comments on commit 6190139

Please sign in to comment.