-
Notifications
You must be signed in to change notification settings - Fork 1
/
macro.py
152 lines (113 loc) · 3.75 KB
/
macro.py
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
import sys
import re
from util import *
from error import errors, warnings, infos
import substitution
Substitution = substitution.Substitution
class ParameterList:
def __init__(self, parameters, length):
self.parameters = parameters
self.length = length
def getPayload(dirsearch, line, parameter_count):
pos = dirsearch.end(2)+1
# search to the end of the parameter list
if parameter_count != 0:
while True:
if pos>=len(line):
return ""
c = line[pos]
if c == ')':
pos += 1
break
pos += 1
# take the reminder of the line and strip out newlines & whitespace
return line[pos:].strip()
# returns a macro definition parameter list
def getParameterList(group, line):
trimmed = stripComments(line)
def_len = len(group)
parameters = trimmed[def_len:]
# find each parameter name separated by a comma
pos = def_len
comma_separation = re.compile(r"\(([^()]*)\)")
reslut = comma_separation.search(line, pos)
parameters = []
paramList = ParameterList(parameters, def_len)
# the macro parameter list needs to start right after the macro identifier
if trimmed[pos] != '(':
return paramList
if reslut == None:
# no parameters --> just a flag
pass
else:
# split string to words and strip leading and trailing whitespace
paramList.parameters = [word.strip() for word in reslut.group(1).split(",")]
#print("params: " + str(parameters))
return paramList
class Macro:
def __init__(self, dirsearch, line):
self.name = ""
self.parameters = []
self.payload = ""
self.nameregex = None
self.flag = False # if this macro is just a flag, not a substitution
self.oneliner = False # used by multiline operation
if dirsearch == None:
return
directive = dirsearch.group(1)
identifier = dirsearch.group(2)
if directive == "" or identifier == "":
#error(row, "Invalid macro directive")
return None
self.name = identifier
self.nameregex = re.compile(r"(\W|^)+(?P<name>" + self.name + r")(\W|$)+")
self.parameters = getParameterList(dirsearch.group(0), line).parameters
self.payload = getPayload(dirsearch, line, len(self.parameters))
def __str__(self):
return "{" + str(self.name) + ", " + str(self.parameters) + ", '" + str(self.payload) + "'}"
def __repr__(self):
return str(self)
def expand(self, match):
# first we try to find the given arguments from the matched line, if needed
# after that we find StringPositions from the payload
# then we substitute each parameter match in the payload (not inside strings)
start = match.start('name')
end = match.end('name')
identifier = match.group('name')
#print("MATCH1: " + match.group(1))
subs = Substitution(match.group('name'), start, end)
subs.string = ""
subs.start = start
if self.parameters:
c = readchar(match.string, end)
if not c or c != '(' and self.flag == False:
errors.add(None, "No parameters for " + self.name)
return False
args = parseArgumentList(end, match.string)
subs.end = end + args.length
#print("namematch: " + str(args.arr))
replacement = self.payload
strings = scanForStrings(replacement)
if len(args.arr) != len(self.parameters):
errors.add(None, "Invalid parameters")
return None
for i, p in enumerate(self.parameters):
reg = re.compile(r"(?P<name>(?P<hash>#)?" + p + r")(\W|$)+")
while True:
hits = 0
namematch = reg.finditer(replacement)
for n in namematch:
if checkIfInsideString(n.start('name'), strings):
continue
val = args.arr[i]
if n.group('hash'):
val = '"' + val + '"'
replacement = n.string[:n.start('name')] + val + n.string[n.end('name'):]
hits += 1
break
if hits == 0:
break
subs.string = replacement
else:
subs.string = self.payload
return subs