1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
#
# include_preprocessor.py
#
# Short pyparsing script to perform #include inclusions similar to the C preprocessor
#
# Copyright 2019, Paul McGuire
#
import pyparsing as pp
from pathlib import Path
# parser elements to be used to assemble into #include parser
SEMI = pp.Suppress(";")
INCLUDE = pp.Keyword("#include")
quoted_string = pp.quoted_string.add_parse_action(pp.remove_quotes)
file_ref = quoted_string | pp.Word(pp.printables, exclude_chars=";")
# parser for parsing "#include xyz.dat;" directives
include_directive = INCLUDE + file_ref("include_file_name") + SEMI
# add parse action that will recursively pull in included files - when
# using transform_string, the value returned from the parse action will replace
# the text matched by the attached expression
seen = set()
def read_include_contents(s, l, t):
include_file_ref = t.include_file_name
include_echo = "/* {} */".format(pp.line(l, s).strip())
# guard against recursive includes
if include_file_ref not in seen:
seen.add(include_file_ref)
included_file_contents = Path(include_file_ref).read_text()
return (
include_echo
+ "\n"
+ include_directive.transform_string(included_file_contents)
)
else:
lead = " " * (pp.col(l, s) - 1)
return "/* recursive include! */\n{}{}".format(lead, include_echo)
# attach include processing method as parse action (parse-time callback)
# to include_directive expression
include_directive.add_parse_action(read_include_contents)
if __name__ == "__main__":
# demo
# create test files:
# - a.txt includes b.txt
# - b.txt includes c.txt
# - c.txt includes b.txt (must catch infinite recursion)
Path("a.txt").write_text(
"""\
/* a.txt */
int i;
/* sometimes included files aren't in quotes */
#include b.txt;
"""
)
Path("b.txt").write_text(
"""\
i = 100;
#include 'c.txt';
"""
)
Path("c.txt").write_text(
"""\
i += 1;
/* watch out! this might be recursive if this file included by b.txt */
#include b.txt;
"""
)
# use include_directive.transform_string to perform includes
# read contents of original file
initial_file = Path("a.txt").read_text()
# print original file
print(initial_file)
print("-----------------")
# expand includes in source file (and any included files) and print the result
expanded_source = include_directive.transform_string(initial_file)
print(expanded_source)
# clean up
for fname in "a.txt b.txt c.txt".split():
Path(fname).unlink()
|