Skip to content

Commit

Permalink
initial somewhat working parser
Browse files Browse the repository at this point in the history
TODO:
ask damian about equations
ask if using lower on constant names is okay
potential third pass
  • Loading branch information
sebastian-swob committed Dec 20, 2024
1 parent 3f77afd commit 1e0b473
Show file tree
Hide file tree
Showing 13 changed files with 14,065 additions and 2 deletions.
Empty file added examples/testing/__init__.py
Empty file.
1 change: 1 addition & 0 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ disable=raw-checker-failed,
missing-function-docstring,
too-few-public-methods,
arguments-differ,
eval-used,


# Enable the message, report, category or checker with the given id(s). You can
Expand Down
Empty file.
238 changes: 238 additions & 0 deletions pytrnsys_process/deck/ddck.lark
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
?start: ddck

ddck: block+

?block: simulation_control | component_control | listing_control

?simulation_control: version | simulation | tolerances | limits
| nan_check | overwrite_check | time_report | dfq | eqsolver | solver |
| assign | end | equations | constants | accelerate

simulation: "SIMULATION"i tstart tend delta_t

tstart: number_or_explicit_var
tend: number_or_explicit_var
delta_t: number_or_explicit_var

tolerances: "TOLERANCES"i ("-" integration_tol "-" convergance_tol | integration_tol convergance_tol)

integration_tol: number_or_explicit_var
convergance_tol: number_or_explicit_var

limits: "LIMITS"i max_iterations_per_time_step max_warnings [trace_component_after_iterations]

max_iterations_per_time_step: positive_int_or_explicit_var
max_warnings: positive_int_or_explicit_var
trace_component_after_iterations: positive_int_or_explicit_var

nan_check: "NAN_CHECK"i bool_or_explicit_var

nan_check_value: bool_or_explicit_var

overwrite_check: "OVERWRITE_CHECK"i bool_or_explicit_var

time_report: "TIME_REPORT"i bool_or_explicit_var

dfq: "DFQ"i positive_int_or_explicit_var

eqsolver: "EQSOLVER"i positive_int_or_explicit_var

solver: "SOLVER"i positive_int_or_explicit_var [relax_factor_min relax_factor_max]

relax_factor_min: number_or_explicit_var
relax_factor_max: number_or_explicit_var

end: "END"i

?listing_control: width | list | nolist | etrace

width: "WIDTH"i positive_int_or_explicit_var -> width

list: "LIST"i

nolist: "NOLIST"i

etrace: "ETRACE"i ton toff

ton: number_or_explicit_var
toff: number_or_explicit_var

?component_control: unit

version: "VERSION"i version_number

version_number: positive_int_or_explicit_var

accelerate: "ACCELERATE"i positive_int_or_explicit_var (positive_int_or_explicit_var "," positive_int_or_explicit_var)+

unit: header [parameters] [inputs [labels]] [trace]

header: "UNIT"i unit_number "TYPE"i type_number [header_comment]

header_comment: HEADER_COMMENT

unit_number: POSITIVE_INT

type_number: POSITIVE_INT

parameters: PARAMETERS ("0" | ((HASH| number_of_parameters) parameter+))

HASH: "#"

PARAMETERS: "parameters"i

number_of_parameters: positive_int_or_explicit_var

parameter: variable | SIGNED_NUMBER

inputs: INPUTS (HASH| number_of_inputs) input+

INPUTS: "inputs"i

number_of_inputs: positive_int_or_explicit_var

input: variable
| SIGNED_NUMBER
| ("0" "," "0" | "CONST")

labels: LABELS (HASH| number_of_labels) label+

LABELS: "labels"i

label: LABEL

number_of_labels: POSITIVE_INT

constants: CONSTANTS (HASH| number_of_constants) equation+

CONSTANTS: "constants"i

number_of_constants: POSITIVE_INT

equations: EQUATIONS (HASH| number_of_equations) equation+

EQUATIONS: "equations"i

number_of_equations: POSITIVE_INT

equation: assignment_target "=" sum

trace: "TRACE"i ton toff

?assignment_target: explicit_var | computed_output_var | unreferencable_var

?sum: product
| sum "+" product -> plus
| sum "-" product -> minus

?product: power
| product "*" power -> times
| product "/" power -> divided_by

?power: atom
| power ("^" | "**") atom -> to_power_of

?atom: NUMBER -> number
| "-" atom -> negate
| variable
| output
| "(" sum ")"
| func_call

?number_or_explicit_var: NUMBER -> value
| explicit_var

?positive_int_or_explicit_var: POSITIVE_INT -> value
| explicit_var

?bool_or_explicit_var: BOOL -> value
| explicit_var

func_call: func_name func_args -> func_call

func_args: "(" (sum ("," sum)* )? ")"

func_name: NAME

output: "[" unit_number "," output_number "]"

output_number: INT

?variable: computed_var
| explicit_var

explicit_var: default_visibility_var
| local_var
| global_var

unreferencable_var: UNREFERENCABLE_NAME

local_var: ":" NAME

global_var: "$" NAME

default_visibility_var: NAME

assign: "ASSIGN"i (file_path | "\"" file_path "\"") logical_unit

file_path: FILE_PATH

logical_unit: INT | explicit_var

computed_var: PORT_PROPERTY "(" PORT_NAME ["," DEFAULT_VARIABLE_NAME ]")"

?computed_output_var: computed_output_temp_var | computed_output_energy_var

computed_output_temp_var: "@temp" "(" PORT_NAME ["," DEFAULT_VARIABLE_NAME ] ")"

computed_output_energy_var: "@energy" "(" ENERGY_DIRECTION "," ENERGY_QUALITY "," CATEGORY_OR_LOCAL ("," CATEGORY)* ")"

ENERGY_DIRECTION: "in" | "out"

ENERGY_QUALITY: "heat" | "el"

CATEGORY: /[A-za-z0-9]+/

CATEGORY_LOCAL: ":"

CATEGORY_OR_LOCAL: CATEGORY | CATEGORY_LOCAL

FILE_PATH: /[A-Za-z0-9_\-\\:\.$]+/

PORT_NAME: NAME

PORT_PROPERTY: "@" ("temp" | "revtemp" | "mfr" | "cp" | "rho")

DEFAULT_VARIABLE_NAME: NAME

NAME: /(?!(constants|equations|unit|parameters|inputs|labels|trace|assign)\s)[a-z]([a-z]|[0-9]|_)*/i

LABEL: /(?!(constants|equations|unit|parameters|inputs|labels|trace|assign)\s)("[^"]+"|[^\s\*!]+)/i

UNREFERENCABLE_NAME: /(?!(constants|equations|unit|parameters|inputs|labels|trace|assign)\s)[a-z]([a-z]|[0-9]|_|-)*/i

POSITIVE_INT: POSITIVE_DIGIT DIGIT*

POSITIVE_DIGIT: "1".."9"

BOOL: "0" | "1"

SIGNED_NUMBER: ("+"?|"-") NUMBER

COMMENT: /^\s*\*[^\n]*\n?/m | /\![^\n]*/

HEADER_COMMENT: /[^!\n]+/

%import common.INT

%import common.NUMBER

%import common.LETTER

%import common.DIGIT

%import common.WS

%ignore COMMENT

%ignore WS
90 changes: 90 additions & 0 deletions pytrnsys_process/deck/extractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import lark as _lark


class ConstantsVisitor(_lark.Visitor):
def __init__(self):
self.resolved_constants: dict = {}

def constants(self, tree: _lark.Tree):
constants_transformer = ConstantsTransformer()
visitor = constants_transformer.transform(tree)

unresolved = {}
for equation in visitor:
if equation and "=" in equation:
key, value = equation.split("=")
try:
evaluated_value = eval(value)
self.resolved_constants[key] = evaluated_value
except NameError:
unresolved[key] = value

while unresolved:
resolved_this_pass = False
for key, value in list(unresolved.items()):
try:
evaluated_value = eval(
value, {"__builtins__": {}}, self.resolved_constants
)
self.resolved_constants[key] = evaluated_value
del unresolved[key]
resolved_this_pass = True
except NameError:
print(f"\nCould not be resolved after pass: {unresolved}")
continue

if not resolved_this_pass and unresolved:
print(f"\nCould not be resolved: {unresolved}")
break


class ConstantsTransformer(_lark.Transformer):

def equation(self, items):
return f"{items[0]}={items[1]}"

def explicit_var(self, items):
return str(items[0])

def default_visibility_var(self, items):
# Using lower because the same constant is written in upper and lower in some places
return str(items[0]).lower()

def func_call(self, items):
func_name = items[0]
args = items[1]
return f"{func_name}({','.join(args)})"

def func_name(self, items):
return items[0]

def func_args(self, items):
return items

def divided_by(self, items):
return f"{items[0]}/{items[1]}"

def times(self, items):
return f"{items[0]}*{items[1]}"

def to_power_of(self, items):
return f"{items[0]}**{items[1]}"

def plus(self, items):
return f"{items[0]}+{items[1]}"

def minus(self, items):
return f"{items[0]}-{items[1]}"

def negate(self, items):
return f"-{items[0]}"

def number(self, items):
return str(items[0])

def start(self, items):
return "\n".join(items)

def constants(self, items):
# Skip the "CONSTANTS" token and number_of_constants
return [x for x in items[2:] if x is not None]
18 changes: 18 additions & 0 deletions pytrnsys_process/deck/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pkgutil as _pu

import lark as _lark

from pytrnsys_process import deck


def _create_parser() -> _lark.Lark:
data = _pu.get_data(deck.__name__, "ddck.lark")
assert data, "Could not find ddck Lark grammar file."
grammar = data.decode()
parser = _lark.Lark(grammar, parser="earley", propagate_positions=True)
return parser


def parse_dck(ddck_content: str) -> _lark.Tree:
tree = _create_parser().parse(ddck_content)
return tree
13 changes: 13 additions & 0 deletions pytrnsys_process/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,16 @@ def convert_svg_to_emf(file_no_suffix: _pl.Path) -> None:
)
except OSError as e:
logger.error("System error running Inkscape: %s", e, exc_info=True)


def get_file_content_as_string(file_path: _pl.Path) -> str:
"""Read and return the entire content of a file as a string.
Args:
file_path (Path): Path to the file to read
Returns:
str: Content of the file as a string
"""
with open(file_path, "r", encoding="UTF-8") as file:
return file.read()
1 change: 1 addition & 0 deletions requirements/release-3rd-party.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pandas
pyarrow
matplotlib
lark
4 changes: 3 additions & 1 deletion requirements/release-3rd-party.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SHA1:809cb076c0830d4cf8c273b940432c33687509d4
# SHA1:a33acdefc3e1de2a6bef7ec8087b3689c1764f9f
#
# This file is autogenerated by pip-compile-multi
# To update, run:
Expand All @@ -13,6 +13,8 @@ fonttools==4.55.3
# via matplotlib
kiwisolver==1.4.7
# via matplotlib
lark==1.2.2
# via -r requirements\release-3rd-party.in
matplotlib==3.10.0
# via -r requirements\release-3rd-party.in
numpy==2.2.0
Expand Down
Loading

0 comments on commit 1e0b473

Please sign in to comment.