Skip to content

Commit

Permalink
Merge pull request #25 from wpbonelli/lark
Browse files Browse the repository at this point in the history
EBNF grammar for mf6 input format. with it Lark can parse a syntax tree from the input string/file with a structure matching the input component hierarchy. this is very rough but it is working minimally. not sure if we want to go this route, just experimenting
  • Loading branch information
wpbonelli authored Sep 5, 2024
2 parents a0a112d + 03f1401 commit e97e06b
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 5 deletions.
68 changes: 68 additions & 0 deletions flopy4/lark/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from lark import Lark

MF6_GRAMMAR = r"""
?start: _NL* _item*
_item: (block | COMMENT) _NL+
// block
block: _begin _NL params _end
_begin: _BEGIN name [index]
_end: _END name
name: WORD
index: INT
_BEGIN: "begin"i
_END: "end"i
// parameter
params: (param _NL)*
param: _key [_value]
_key: KEYS
_value: NUMBER | path | string | array | list
// string
string: WORD+
// file path
path: INOUT PATH
PATH: [_PATHSEP] (NON_SEPARATOR_STRING [_PATHSEP]) [NON_SEPARATOR_STRING]
_PATHSEP: "/"
INOUT: "filein"i|"fileout"i
// array
array: constantarray | internalarray | externalarray
constantarray: "CONSTANT" NUMBER
internalarray: "INTERNAL" [factor] [iprn] (NUMBER* [_NL])*
externalarray: "OPEN/CLOSE" WORD [factor] ["binary"] [iprn]
factor: "FACTOR" NUMBER
iprn: "IPRN" INT
// list (adapted from https://github.com/lark-parser/lark/blob/master/examples/composition/csv.lark)
list: header _NL row*
header: "#" " "? (WORD _SEPARATOR?)+
row: (_anything _SEPARATOR?)+ _NL
_anything: INT | WORD | NON_SEPARATOR_STRING | FLOAT | SIGNED_FLOAT
NON_SEPARATOR_STRING: /[a-zA-z.;\\\/]+/
_SEPARATOR: /[ ]+/
| "\t"
| ","
// newline
_NL: /(\r?\n[\t ]*)+/
// parameter keys file can be generated
// with the rest of the plugin interface
// and maybe placed in a separate file
KEYS: "K"|"I"|"D"|"S"|"F"|"A"
%import common.SH_COMMENT -> COMMENT
%import common.SIGNED_NUMBER -> NUMBER
%import common.SIGNED_FLOAT
%import common.INT
%import common.FLOAT
%import common.WORD
%import common.WS_INLINE
%ignore WS_INLINE
"""

MF6_PARSER = Lark(MF6_GRAMMAR, start="start")
2 changes: 0 additions & 2 deletions flopy4/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ class MFParamSpec:
repeating: bool = False
tagged: bool = True
reader: MFReader = MFReader.urword
# todo change to variadic tuple of str and resolve
# actual shape at load time from simulation context
shape: Optional[Tuple[int]] = None
default_value: Optional[Any] = None

Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
"attrs", # todo: lower bound?
"cattrs", # todo: lower bound?
"Jinja2>=3.0",
"attrs", # todo: bounds?
"cattrs", # todo: bounds?
"flopy>=3.7.0",
"Jinja2>=3.0",
"lark", # todo: bounds?
"numpy>=1.20.3",
"pandas>=2.0.0",
"toml>=0.10",
Expand All @@ -54,6 +55,7 @@ test = [
"flopy4[lint]",
"coverage",
"GitPython",
"interegular",
"jupyter",
"jupytext",
"modflow-devtools",
Expand Down
42 changes: 42 additions & 0 deletions test/test_lark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from pprint import pprint

import pytest
from lark import Transformer

from flopy4.lark import MF6_PARSER

TEST_PKG = """
BEGIN OPTIONS
K
I 1
D 1.0
S hello world
F FILEIN some/path
END OPTIONS
BEGIN PACKAGEDATA 1
A INTERNAL 1.0 2.0 3.0
END PACKAGEDATA
"""


def test_parse_mf6():
tree = MF6_PARSER.parse(TEST_PKG)
# this is working, check it with:
# pytest test/test_lark.py::test_parse_mf6 -s
print(tree.pretty())


class MF6Transformer(Transformer):
# TODO
pass


MF6_TRANSFORMER = MF6Transformer()


@pytest.mark.xfail
def test_transform_mf6():
tree = MF6_PARSER.parse(TEST_PKG)
data = MF6_TRANSFORMER.transform(tree)
pprint(data)

0 comments on commit e97e06b

Please sign in to comment.