Skip to content

Commit 7cce3e2

Browse files
committed
.
1 parent 3c27b83 commit 7cce3e2

File tree

3 files changed

+317
-314
lines changed

3 files changed

+317
-314
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/// <summary>
2+
/// Parses and evaluates preprocessor expressions, handling known and unknown symbols.
3+
/// </summary>
4+
public class ExpressionParser(string expression, HashSet<string> definedSymbols, HashSet<string> knownSymbols)
5+
{
6+
int pos;
7+
8+
public TriState Evaluate()
9+
{
10+
pos = 0;
11+
return ParseOr();
12+
}
13+
14+
public string Simplify()
15+
{
16+
pos = 0;
17+
return SimplifyOr();
18+
}
19+
20+
void SkipWhitespace()
21+
{
22+
while (pos < expression.Length && char.IsWhiteSpace(expression[pos]))
23+
{
24+
pos++;
25+
}
26+
}
27+
28+
TriState ParseOr()
29+
{
30+
var left = ParseAnd();
31+
SkipWhitespace();
32+
33+
while (pos < expression.Length - 1 && expression.Substring(pos, 2) == "||")
34+
{
35+
pos += 2;
36+
var right = ParseAnd();
37+
left = OrTriState(left, right);
38+
}
39+
40+
return left;
41+
}
42+
43+
TriState ParseAnd()
44+
{
45+
var left = ParseUnary();
46+
SkipWhitespace();
47+
48+
while (pos < expression.Length - 1 && expression.Substring(pos, 2) == "&&")
49+
{
50+
pos += 2;
51+
var right = ParseUnary();
52+
left = AndTriState(left, right);
53+
}
54+
55+
return left;
56+
}
57+
58+
TriState ParseUnary()
59+
{
60+
SkipWhitespace();
61+
62+
if (pos < expression.Length && expression[pos] == '!')
63+
{
64+
pos++;
65+
var inner = ParseUnary();
66+
return NotTriState(inner);
67+
}
68+
69+
return ParsePrimary();
70+
}
71+
72+
TriState ParsePrimary()
73+
{
74+
SkipWhitespace();
75+
76+
if (pos < expression.Length && expression[pos] == '(')
77+
{
78+
pos++;
79+
var result = ParseOr();
80+
SkipWhitespace();
81+
if (pos < expression.Length && expression[pos] == ')')
82+
{
83+
pos++;
84+
}
85+
86+
return result;
87+
}
88+
89+
// Parse identifier
90+
var start = pos;
91+
while (pos < expression.Length && (char.IsLetterOrDigit(expression[pos]) || expression[pos] == '_'))
92+
{
93+
pos++;
94+
}
95+
96+
var identifier = expression.Substring(start, pos - start);
97+
SkipWhitespace();
98+
99+
if (string.Equals(identifier, "true", StringComparison.OrdinalIgnoreCase))
100+
{
101+
return TriState.True;
102+
}
103+
104+
if (string.Equals(identifier, "false", StringComparison.OrdinalIgnoreCase))
105+
{
106+
return TriState.False;
107+
}
108+
109+
// Check if this is a known symbol
110+
if (knownSymbols.Contains(identifier))
111+
{
112+
return definedSymbols.Contains(identifier) ? TriState.True : TriState.False;
113+
}
114+
115+
// Unknown symbol
116+
return TriState.Unknown;
117+
}
118+
119+
// Simplification methods
120+
string SimplifyOr()
121+
{
122+
var left = SimplifyAnd();
123+
SkipWhitespace();
124+
var parts = new List<string> {left};
125+
126+
while (pos < expression.Length - 1 && expression.Substring(pos, 2) == "||")
127+
{
128+
pos += 2;
129+
var right = SimplifyAnd();
130+
parts.Add(right);
131+
}
132+
133+
// Simplify: if any part is "true", result is "true"
134+
// If any part is "false", remove it
135+
var filtered = parts.Where(p => p != "false").ToList();
136+
if (filtered.Any(p => p == "true"))
137+
{
138+
return "true";
139+
}
140+
141+
if (filtered.Count == 0)
142+
{
143+
return "false";
144+
}
145+
146+
if (filtered.Count == 1)
147+
{
148+
return filtered[0];
149+
}
150+
151+
return string.Join(" || ", filtered);
152+
}
153+
154+
string SimplifyAnd()
155+
{
156+
var left = SimplifyUnary();
157+
SkipWhitespace();
158+
var parts = new List<string> {left};
159+
160+
while (pos < expression.Length - 1 && expression.Substring(pos, 2) == "&&")
161+
{
162+
pos += 2;
163+
var right = SimplifyUnary();
164+
parts.Add(right);
165+
}
166+
167+
// Simplify: if any part is "false", result is "false"
168+
// If any part is "true", remove it
169+
if (parts.Any(p => p == "false"))
170+
{
171+
return "false";
172+
}
173+
174+
var filtered = parts.Where(p => p != "true").ToList();
175+
if (filtered.Count == 0)
176+
{
177+
return "true";
178+
}
179+
180+
if (filtered.Count == 1)
181+
{
182+
return filtered[0];
183+
}
184+
185+
return string.Join(" && ", filtered);
186+
}
187+
188+
string SimplifyUnary()
189+
{
190+
SkipWhitespace();
191+
192+
if (pos < expression.Length && expression[pos] == '!')
193+
{
194+
pos++;
195+
var inner = SimplifyUnary();
196+
if (inner == "true")
197+
{
198+
return "false";
199+
}
200+
201+
if (inner == "false")
202+
{
203+
return "true";
204+
}
205+
206+
// Check if inner starts with ! and simplify double negation
207+
if (inner.StartsWith('!'))
208+
{
209+
return inner[1..];
210+
}
211+
212+
return $"!{inner}";
213+
}
214+
215+
return SimplifyPrimary();
216+
}
217+
218+
string SimplifyPrimary()
219+
{
220+
SkipWhitespace();
221+
222+
if (pos < expression.Length && expression[pos] == '(')
223+
{
224+
pos++;
225+
var result = SimplifyOr();
226+
SkipWhitespace();
227+
if (pos < expression.Length && expression[pos] == ')')
228+
{
229+
pos++;
230+
}
231+
232+
// Only keep parens if needed (contains || and was inside an && context)
233+
if (result.Contains("||") && !result.StartsWith('('))
234+
{
235+
return $"({result})";
236+
}
237+
238+
return result;
239+
}
240+
241+
// Parse identifier
242+
var start = pos;
243+
while (pos < expression.Length && (char.IsLetterOrDigit(expression[pos]) || expression[pos] == '_'))
244+
{
245+
pos++;
246+
}
247+
248+
var identifier = expression[start..pos];
249+
SkipWhitespace();
250+
251+
if (string.Equals(identifier, "true", StringComparison.OrdinalIgnoreCase))
252+
{
253+
return "true";
254+
}
255+
256+
if (string.Equals(identifier, "false", StringComparison.OrdinalIgnoreCase))
257+
{
258+
return "false";
259+
}
260+
261+
// Check if this is a known symbol - replace with true/false
262+
if (knownSymbols.Contains(identifier))
263+
{
264+
return definedSymbols.Contains(identifier) ? "true" : "false";
265+
}
266+
267+
// Unknown symbol - keep as-is
268+
return identifier;
269+
}
270+
271+
static TriState AndTriState(TriState left, TriState right)
272+
{
273+
if (left == TriState.False || right == TriState.False)
274+
{
275+
return TriState.False;
276+
}
277+
278+
if (left == TriState.True && right == TriState.True)
279+
{
280+
return TriState.True;
281+
}
282+
283+
return TriState.Unknown;
284+
}
285+
286+
static TriState OrTriState(TriState left, TriState right)
287+
{
288+
if (left == TriState.True || right == TriState.True)
289+
{
290+
return TriState.True;
291+
}
292+
293+
if (left == TriState.False && right == TriState.False)
294+
{
295+
return TriState.False;
296+
}
297+
298+
return TriState.Unknown;
299+
}
300+
301+
static TriState NotTriState(TriState value) =>
302+
value switch
303+
{
304+
TriState.True => TriState.False,
305+
TriState.False => TriState.True,
306+
_ => TriState.Unknown
307+
};
308+
}

0 commit comments

Comments
 (0)