Skip to content

Commit ac6d89f

Browse files
authored
Merge pull request #53 from erezsh/lark_cache
Use Lark with its cache feature, instead of creating a standalone parser
2 parents ab97c39 + 37e5f2b commit ac6d89f

4 files changed

Lines changed: 19 additions & 39 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,4 @@ node_modules/
120120

121121
# Don't commit the generated parser
122122
lark_parser.py
123+
.lark_cache.bin

hcl2/api.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,7 @@ def loads(text: str) -> dict:
1515
# Lark doesn't support a EOF token so our grammar can't look for "new line or end of file"
1616
# This means that all blocks must end in a new line even if the file ends
1717
# Append a new line as a temporary fix
18-
return hcl2.parse(text + "\n")
18+
# Ignoring type as the type-annotation of Lark.parse() claims that it always returns a Tree,
19+
# but in the docs of the parse() said that it returns whatever the supplied transformer returns.
20+
# We supply DictTransformer so the return type is Dict.
21+
return hcl2.parse(text + "\n") # type: ignore

hcl2/parser.py

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,18 @@
11
"""A parser for HCL2 implemented using the Lark parser"""
22
from pathlib import Path
33

4-
from hcl2.transformer import DictTransformer
5-
6-
THIS_DIR = Path(__file__).absolute().resolve().parent
7-
PARSER_FILE = THIS_DIR / "lark_parser.py"
8-
4+
from lark import Lark
95

10-
def create_parser_file(parser_file: Path = PARSER_FILE) -> None:
11-
"""
12-
Parsing the Lark grammar takes about 0.5 seconds. In order to improve performance we can cache the parser
13-
file. The below code caches the entire python file which is generated by Lark's standalone parser feature
14-
See: https://github.com/lark-parser/lark/blob/master/lark/tools/standalone.py
15-
16-
Lark also supports serializing the parser config but the deserialize function did not work for me.
17-
The lark state contains dicts with numbers as keys which is not supported by json so the serialized
18-
state can't be written to a json file. Exporting to other file types would have required
19-
adding additional dependencies or writing a lot more code. Lark's standalone parser
20-
feature works great but it expects to be run as a separate shell command
21-
The below code copies some of the standalone parser generator code in a way that we can use
22-
"""
23-
# This function is needed only if standalone parser is not yet compiled.
24-
# pylint: disable=import-outside-toplevel
25-
from lark import Lark
26-
from lark.tools.standalone import gen_standalone
27-
28-
lark_file = THIS_DIR / "hcl2.lark"
29-
with open(parser_file, "w", encoding="utf-8") as parser_file_stream:
30-
lark_inst = Lark(lark_file.read_text(), parser="lalr", lexer="contextual")
31-
parser_file_stream.write("# mypy: ignore-errors\n")
32-
gen_standalone(lark_inst, out=parser_file_stream)
6+
from hcl2.transformer import DictTransformer
337

348

35-
if not PARSER_FILE.exists():
36-
create_parser_file(PARSER_FILE)
9+
PARSER_FILE = Path(__file__).absolute().resolve().parent / ".lark_cache.bin"
3710

38-
# pylint: disable=wrong-import-position
39-
# Lark_StandAlone needs to be imported after the above block of code because lark_parser.py might not exist
40-
from hcl2.lark_parser import Lark_StandAlone
4111

42-
hcl2 = Lark_StandAlone(transformer=DictTransformer())
12+
hcl2 = Lark.open(
13+
"hcl2.lark",
14+
parser="lalr",
15+
cache=str(PARSER_FILE), # Disable/Delete file to effect changes to the grammar
16+
rel_to=__file__,
17+
transformer=DictTransformer(),
18+
)

test/unit/test_load.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
""" Test parsing a variety of hcl files"""
2+
23
import json
34
from pathlib import Path
45
from unittest import TestCase
56

7+
from hcl2.parser import PARSER_FILE
68
import hcl2
7-
from hcl2.parser import PARSER_FILE, create_parser_file
89

910

1011
HELPERS_DIR = Path(__file__).absolute().parent.parent / "helpers"
@@ -18,9 +19,8 @@ class TestLoad(TestCase):
1819

1920
def test_load_terraform(self):
2021
"""Test parsing a set of hcl2 files and force recreating the parser file"""
21-
parser_file = Path(hcl2.__file__).absolute().parent / PARSER_FILE
22-
parser_file.unlink()
23-
create_parser_file()
22+
# delete the parser file to force it to be recreated
23+
PARSER_FILE.unlink()
2424
for hcl_path in HCL2_FILES:
2525
yield self.check_terraform, hcl_path
2626

0 commit comments

Comments
 (0)