File: simplecpp

package info (click to toggle)
freedoom 0.13.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 48,712 kB
  • sloc: python: 3,513; makefile: 529; xml: 188; sh: 73
file content (201 lines) | stat: -rwxr-xr-x 3,940 bytes parent folder | download
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
#
# simple cpp-style preprocessor
#
# Understands:
#
# #define NAME
#
# Set an option
# You can use -D on the command line too
#
# #undef NAME
#
# Unset an option if it is set
#
# #if .. #endif / #ifdef .. #endif
#
# Specify a list of options set, eg #ifdef PHASE1 || PHASE2
# The block is only displayed if one of the options is set
#
# #ifn .. #endif / #ifndef .. #endif
#
# Similarly specify a list of options
# The block is displayed if none of the options are set
#
# #include "filename"
#
# include the contents of a file

import sys
import re

debug = False
defines = {}

command_re = re.compile(r"\#(\w+)(\s+(.*))?")
include_re = re.compile(r'\s*"(.*)"\s*')


def debug_msg(message):
    if debug:
        sys.stderr.write(message)


# Parse command line options


def parse_cmdline():
    for arg in sys.argv[1:]:
        if arg.startswith("-D"):
            name = arg[2:]
            defines[name] = True


def parse_stream(stream):
    result = read_block(stream, False)

    if result is not None:
        raise Exception("Mismatched #if in '%s'" % stream.name)


def parse_file(filename):
    f = open(filename)

    try:
        parse_stream(f)
    finally:
        f.close()


# #include


def cmd_include(arg):
    # Extract the filename

    match = include_re.match(arg)

    if not match:
        raise Exception("Invalid 'include' command")

    filename = match.group(1)

    # Open the file and process it

    parse_file(filename)


# #define


def cmd_define(arg):
    defines[arg] = True


# #undef


def cmd_undef(arg):
    if arg in defines:
        del defines[arg]


# #ifdef/#ifndef


def cmd_ifdef(arg, command, stream, ignore):

    # Get the define name
    name = arg.strip()

    debug_msg("%s %s >\n" % (command, arg))

    # Should we ignore the contents of this block?

    sub_ignore = name not in defines

    if "n" in command:
        sub_ignore = not sub_ignore

    # Parse the block

    result = read_block(stream, ignore or sub_ignore)

    debug_msg("%s %s < (%s)\n" % (command, arg, result))

    # There may be a second "else" block to parse:

    if result == "else":
        debug_msg("%s %s else >\n" % (command, arg))
        result = read_block(stream, ignore or (not sub_ignore))
        debug_msg("%s %s else < (%s)\n" % (command, arg, result))

    # Should end in an endif:

    if result != "endif":
        raise Exception("'if' block did not end in an 'endif'")


commands = {
    "include": cmd_include,
    "define": cmd_define,
    "undef": cmd_undef,
    "if": cmd_ifdef,
    "ifdef": cmd_ifdef,
    "ifn": cmd_ifdef,
    "ifndef": cmd_ifdef,
}

# Recursive block reading function
# if 'ignore' argument is 1, contents are ignored


def read_block(stream, ignore):

    for line in stream:

        # Remove newline

        line = line[0:-1]

        # Ignore, but keep empty lines

        if line == " " * len(line):
            print(line)
            continue

        # Check if this line has a command

        match = command_re.match(line)

        if match:
            command = match.group(1)
            arg = match.group(3)

            if command == "else" or command == "endif":
                return command
            elif command not in commands:
                raise Exception("Unknown command: '%s'" % command)

            # Get the callback function.

            func = commands[command]

            # Invoke the callback function. #ifdef commands
            # are a special case and need extra arguments.
            # Other commands are only executed if we are not
            # ignoring this block.

            if func == cmd_ifdef:
                cmd_ifdef(arg, command=command, stream=stream, ignore=ignore)
            elif not ignore:
                func(arg)
        else:
            if not ignore:
                print(line)


parse_cmdline()
parse_stream(sys.stdin)