Skip to content

Commit 8109641

Browse files
Redo command-line interface
1 parent d6954ed commit 8109641

File tree

4 files changed

+95
-46
lines changed

4 files changed

+95
-46
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
__pycache__
22
dist
3-
vhdlproc.egg-info
3+
vhdlproc.egg-info
4+
*.proc.vhdl

README.md

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,48 @@ $ ./vhdlproc/vhdlproc.py --help
3030
### Command Line
3131

3232
```
33-
usage: vhdlproc.py [-h] [-i I] [-o O] [-D IDENTIFIER=value]
33+
usage: vhdlproc.py [-h] [-D IDENTIFIER=value] [-o DIRECTORY] [-e EXTENSION] [--self-test]
34+
[--log-level LEVEL]
35+
[input ...]
3436
35-
VHDLproc v1.2.0 - VHDL Preprocessor
37+
VHDLproc v2.2 - VHDL Preprocessor
3638
37-
optional arguments:
39+
positional arguments:
40+
input Input files (will skip over files with the output extension)
41+
42+
options:
3843
-h, --help show this help message and exit
39-
-i I Input file (Omit to read from stdin)
40-
-o O Output file (Omit to print to stdout)
4144
-D IDENTIFIER=value Specify identifiers for conditional compilation, ex. DEBUG_LEVEL=2
45+
-o DIRECTORY Directory to store parsed files
46+
-e EXTENSION Output extension for processed files (defaults to '.proc.vhdl')
47+
--self-test Run a self-test to ensure functionality
48+
--log-level LEVEL Configure the logging level
4249
```
4350

44-
You can read from stdin or a file, and print to stdout or another file.
51+
A basic example, where VHDLproc will parse each input file, output the processed text to a new file with a given extension, and the processed files are then passed to GHDL:
4552

53+
```bash
54+
vhdlproc *.vhdl # preprocess all the files
55+
ghdl -a --std=08 *.proc.vhdl # pass processed files to ghdl
56+
ghdl -r --std=08 testbench # run simulation
4657
```
47-
$ cat tests/define.vhdl | python vhdlproc/vhdlproc.py
48-
`if VHDL_VERSION >= "2008" then
49-
...
50-
`end if
51-
...
58+
59+
As VHDLproc also outputs each of the processed filenames to STDOUT, this would also work:
60+
```bash
61+
ghdl -a --std=08 $(vhdlproc *.vhdl)
62+
ghdl -r --std=08 testbench
63+
```
64+
65+
The parsed files can also be stored to another directory:
66+
```bash
67+
vhdlproc *.vhdl -o build/ # preprocess all the files and store in build/
68+
ghdl -a --std=08 build/*.vhdl # pass processed files in build/ to ghdl
69+
ghdl -r --std=08 testbench # run simulation
5270
```
5371

5472
### Python Library
5573

56-
Parse files:
74+
Parse files (will automatically set the include path):
5775

5876
```python
5977
from vhdlproc import VHDLproc
@@ -84,7 +102,7 @@ code = [
84102
'`include "some/file.vhdl"',
85103
]
86104

87-
parsed_text_list = processor.parse_file(code, identifiers=identifiers, include_path="path/to/pull/include/directives/from")
105+
parsed_text = processor.parse(code, identifiers=identifiers, include_path="path/to/pull/include/directives/from")
88106

89107
# Parse string
90108
code = '''
@@ -96,7 +114,7 @@ constant test_var : integer := 100
96114
`include "some/file.vhdl"
97115
'''
98116

99-
parsed_text_str = processor.parse_file(code, identifiers=identifiers, include_path="path/to/pull/include/directives/from")
117+
parsed_text = processor.parse(code, identifiers=identifiers, include_path="path/to/pull/include/directives/from")
100118
```
101119

102120
### Preprocessor Directives (what you put in your VHDL files)
@@ -136,7 +154,7 @@ By default, `TOOL_NAME` is set to `VHDLproc` and `TOOL_VERSION` is set to the cu
136154
- [ ] Prevent a file from including itself (to prevent infinite loops)
137155
- [ ] Modify text and file operations to work on Windows (if they don't already)
138156
- [ ] Throw an error if a `` `warning `` or `` `error `` string isn't wrapped in quotes
139-
- [ ] Add the option to the CLI to take in a series of file inputs, process them, save the individual results to temporary files (i.e. in `/tmp/` or a local path), then return all of the filepaths. This would be useful for doing this with GHDL: `ghdl -a $(vhdlproc -f *.vhdl)`.
157+
- [x] Add the option to the CLI to take in a series of file inputs, process them, save the individual results to temporary files (i.e. in `/tmp/` or a local path), then return all of the filepaths. This would be useful for doing this with GHDL: `ghdl -a $(vhdlproc *.vhdl)`.
140158

141159
## Examples
142160

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
license="GPLv3",
1212
packages=["vhdlproc"],
1313
package_data={"vhdlproc": ["tests/*"]},
14-
install_requires=["argparse", "black"],
14+
install_requires=[],
1515
classifiers=[
1616
"Programming Language :: Python :: 3",
1717
"Programming Language :: Python :: 3.6",

vhdlproc/vhdlproc.py

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
logger = logging.getLogger(__name__)
1010

11-
__version__ = "v2.1"
11+
__version__ = "v2.2"
1212

1313

1414
# Infix class based on https://code.activestate.com/recipes/384122/
@@ -270,7 +270,7 @@ def parse(
270270
if not ifstack[-1][0] and code[line_i].strip()[:2] != "--":
271271
code[line_i] = self._comment_char + code[line_i]
272272

273-
# print(f'ifstack: {ifstack}')
273+
# logging.debug(f'ifstack: {ifstack}')
274274

275275
if len(ifstack) > 1:
276276
raise Exception(f"Line {line_i+1}: Missing `end [ if ]")
@@ -288,7 +288,7 @@ def test_file(name: str, identifiers: Dict[str, str] = {}) -> bool:
288288
Returns:
289289
bool: Whether the test passed or failed
290290
"""
291-
print(f"\n== Testing {name} ==")
291+
logging.info(f"\n== Testing {name} ==")
292292
proc = VHDLproc()
293293
filename = os.path.dirname(__file__) + f"/tests/{name}.vhdl"
294294
passed = True
@@ -308,63 +308,93 @@ def test_file(name: str, identifiers: Dict[str, str] = {}) -> bool:
308308
return passed
309309

310310

311-
def test_all():
311+
def test_all(identifiers: Dict[str, str]):
312312
"""Runs the 'include', 'and', and 'nest' tests
313313
314314
Returns:
315315
bool: Whether the tests passed or failed
316316
"""
317-
return not (test_file("include") and test_file("and") and test_file("nest"))
317+
return not (
318+
test_file("include", identifiers)
319+
and test_file("and", identifiers)
320+
and test_file("nest", identifiers)
321+
)
318322

319323

320324
def _cli():
321325
parser = argparse.ArgumentParser(
322-
description=f"VHDLproc v{__version__} - VHDL Preprocessor"
326+
description=f"VHDLproc {__version__} - VHDL Preprocessor"
323327
)
324-
parser.add_argument("-i", help="Input file (Omit to read from stdin)")
325-
parser.add_argument("-o", help="Output file (Omit to print to stdout)")
326328
parser.add_argument(
327-
"--test", action="store_true", help="Tests VHDLproc to ensure functionality"
329+
"input",
330+
nargs="*",
331+
help="Input files (will skip over files with the output extension)",
328332
)
329333
parser.add_argument(
330334
"-D",
331335
action="append",
332336
metavar="IDENTIFIER=value",
333337
help="Specify identifiers for conditional compilation, ex. DEBUG_LEVEL=2",
334338
)
339+
parser.add_argument(
340+
"-o",
341+
metavar="DIRECTORY",
342+
help="Directory to store parsed files",
343+
)
344+
parser.add_argument(
345+
"-e",
346+
metavar="EXTENSION",
347+
default=".proc.vhdl",
348+
help="Output extension for processed files (defaults to '.proc.vhdl')",
349+
)
350+
parser.add_argument(
351+
"--self-test",
352+
action="store_true",
353+
help="Run a self-test to ensure functionality",
354+
)
335355
parser.add_argument(
336356
"--log-level",
357+
metavar="LEVEL",
337358
default=logging.INFO,
338359
type=(lambda x: getattr(logging, x)),
339-
help="Configure the logging level.",
360+
help="Configure the logging level",
340361
)
341362
args = parser.parse_args()
342363

343364
logging.basicConfig(level=args.log_level, format="%(levelname)s: %(message)s")
344365

345-
if args.test:
346-
retn = test_all()
347-
exit(retn)
366+
identifiers = (
367+
{id.split("=")[0]: str(id.split("=")[1]) for id in args.D} if args.D else {}
368+
)
348369

349-
identifiers = {}
370+
if args.self_test:
371+
test_all(identifiers)
372+
return
350373

351-
if args.D:
352-
for id in args.D:
353-
identifiers[id.split("=")[0]] = str(id.split("=")[1])
374+
if not os.path.exists(args.temp_dir):
375+
os.makedirs(args.temp_dir)
354376

355377
proc = VHDLproc()
356-
if args.i:
357-
parsed_code = proc.parse_file(args.i, identifiers=identifiers)
358-
else:
359-
code = []
360-
for line in sys.stdin:
361-
code.append(line.rstrip("\n"))
362-
parsed_code = proc.parse(code, identifiers=identifiers)
363-
364-
if args.o:
365-
open(args.o, "w+").write(parsed_code)
366-
else:
367-
print(parsed_code)
378+
for file in args.input:
379+
if file.endswith(args.extension):
380+
logging.debug(f"Skipping file {file}")
381+
continue
382+
383+
if args.out_dir is not None:
384+
newfile = os.path.join(
385+
args.out_dir,
386+
os.path.splitext(os.path.basename(file))[0] + args.extension,
387+
)
388+
else:
389+
newfile = os.path.splitext(file)[0] + args.extension
390+
391+
print(newfile)
392+
393+
logging.debug(f"Parsing file {file} to {newfile}")
394+
395+
parsed_code = proc.parse_file(file, identifiers=identifiers)
396+
with open(newfile, "w") as f:
397+
f.write(parsed_code)
368398

369399

370400
if __name__ == "__main__":

0 commit comments

Comments
 (0)