1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 | using System.Linq;
|
---|
5 | using System.Text;
|
---|
6 | using System.Threading.Tasks;
|
---|
7 |
|
---|
8 | namespace CodeGenerator {
|
---|
9 | public class BruteForceCodeGen {
|
---|
10 |
|
---|
11 | private string usings = @"
|
---|
12 | using System.Collections.Generic;
|
---|
13 | using System.Linq;
|
---|
14 | using System;
|
---|
15 | ";
|
---|
16 |
|
---|
17 | private string solverTemplate = @"
|
---|
18 | namespace ?PROBLEMNAME? {
|
---|
19 | private static class Grammar {
|
---|
20 | ?GRAMMARCLASSCODE?
|
---|
21 | }
|
---|
22 | private sealed class PartiallyProcessedSeq {
|
---|
23 | private PartiallyProcessedSeq parent;
|
---|
24 | private int alt;
|
---|
25 | private IEnumerable<string> remaining;
|
---|
26 | public PartiallyProcessedSeq(IEnumerable<string> alternative) {
|
---|
27 | this.remaining = alternative;
|
---|
28 | }
|
---|
29 | public PartiallyProcessedSeq CreateAlternative(int p, IEnumerable<string> alternative) {
|
---|
30 | var child = new PartiallyProcessedSeq(alternative.Concat(remaining));
|
---|
31 | child.parent = this;
|
---|
32 | }
|
---|
33 | public bool TryProcessToNextSymbolWithAlternatives(out string symbol) {
|
---|
34 | remaining = remaining.SkipWhile(s=>Grammar.NumberOfAlternatives(s)==0);
|
---|
35 | if(remaining.Any()) {
|
---|
36 | symbol = remaining.First();
|
---|
37 | remaining = remaining.Skip(1);
|
---|
38 | return true;
|
---|
39 | } else {
|
---|
40 | symbol = null;
|
---|
41 | return false;
|
---|
42 | }
|
---|
43 |
|
---|
44 | }
|
---|
45 | public IEnumerable<int> Path {
|
---|
46 | get {
|
---|
47 | var cur = this;
|
---|
48 | var List<int> path = new List<int>();
|
---|
49 | while(cur.parent!=null) {
|
---|
50 | path.Append(cur.p);
|
---|
51 | cur = cur.parent;
|
---|
52 | }
|
---|
53 | return path.Reverse();
|
---|
54 | }
|
---|
55 | }
|
---|
56 | }
|
---|
57 | public sealed class ?IDENT?Solver {
|
---|
58 |
|
---|
59 | public static void Main(string[] args) {
|
---|
60 | var solver = new ?IDENT?Solver();
|
---|
61 | solver.Start();
|
---|
62 | }
|
---|
63 |
|
---|
64 | public ?IDENT?Solver() {
|
---|
65 | Initialize();
|
---|
66 | }
|
---|
67 |
|
---|
68 | private void Initialize() {
|
---|
69 | ?INITCODE?
|
---|
70 | }
|
---|
71 |
|
---|
72 | private bool IsBetter(double a, double b) {
|
---|
73 | ?MAXIMIZATION? ? a > b : a < b;
|
---|
74 | }
|
---|
75 |
|
---|
76 | private IEnumerable<IEnumerator<int>> GeneratePaths() {
|
---|
77 | var queue = new Queue<PartiallyProcessedSeq>();
|
---|
78 | foreach(var alt in Grammar.GetAlternatives(Grammar.RootSymbol))
|
---|
79 | queue.Enqueue(new PartiallyProcessedSeq(alt));
|
---|
80 |
|
---|
81 | while(queue.Count > 0) {
|
---|
82 | var e = queue.Dequeue();
|
---|
83 | string ntSöymbol;
|
---|
84 | if(e.TryProcessToNextNtSymbol(out ntSymbol)) {
|
---|
85 | int i=0;
|
---|
86 | foreach(var alt in Grammar.GetAlternatives(symbol)) {
|
---|
87 | queue.Enqueue(new e.CreateAlternative(i++, alt));
|
---|
88 | }
|
---|
89 | } else {
|
---|
90 | yield return e.Path;
|
---|
91 | }
|
---|
92 | }
|
---|
93 | }
|
---|
94 |
|
---|
95 | ?PATHGENERATORCODE?
|
---|
96 |
|
---|
97 | private void Start() {
|
---|
98 | // generate possible paths through the grammar and evaluate each of them
|
---|
99 | var bestF = ?MAXIMIZATION? ? double.NegativeInfinity : double.PositiveInfinity;
|
---|
100 | foreach(var path in GeneratePaths()) {
|
---|
101 | currentPath = path;
|
---|
102 | var f = Calculate();
|
---|
103 | if(IsBetter(f, bestF)) bestF = f;
|
---|
104 | Console.WriteLine(""{0}\t{1}"",bestF, f);
|
---|
105 | }
|
---|
106 | }
|
---|
107 |
|
---|
108 | private IEnumerator<int> currentPath;
|
---|
109 |
|
---|
110 | public double Calculate() {
|
---|
111 | try {
|
---|
112 | ?FITNESSFUNCTION?
|
---|
113 | } catch(Exception e) {
|
---|
114 | throw;
|
---|
115 | }
|
---|
116 | }
|
---|
117 |
|
---|
118 | ?ADDITIONALCODE?
|
---|
119 |
|
---|
120 | ?INTERPRETERSOURCE?
|
---|
121 |
|
---|
122 | ?CONSTRAINTSSOURCE?
|
---|
123 | }
|
---|
124 | }";
|
---|
125 |
|
---|
126 |
|
---|
127 | /// <summary>
|
---|
128 | /// Generates the source code for a brute force searcher that can be compiled with a C# compiler
|
---|
129 | /// </summary>
|
---|
130 | /// <param name="ast">An abstract syntax tree for a GPDL file</param>
|
---|
131 | public void Generate(GPDefNode ast) {
|
---|
132 | var problemSourceCode = new StringBuilder();
|
---|
133 | problemSourceCode.AppendLine(usings);
|
---|
134 |
|
---|
135 | GenerateProblem(ast, problemSourceCode);
|
---|
136 |
|
---|
137 | problemSourceCode.Replace("?PROBLEMNAME?", ast.Name);
|
---|
138 |
|
---|
139 | // write to a file for debugging
|
---|
140 | using (var stream = new StreamWriter(ast.Name + ".cs")) {
|
---|
141 | stream.WriteLine(problemSourceCode.ToString());
|
---|
142 | }
|
---|
143 | }
|
---|
144 |
|
---|
145 | private void GenerateProblem(GPDefNode ast, StringBuilder problemSourceCode) {
|
---|
146 | var problemClassCode =
|
---|
147 | solverTemplate
|
---|
148 | .Replace("?MAXIMIZATION?", ast.FitnessFunctionNode.Maximization.ToString())
|
---|
149 | .Replace("?GRAMMARCLASSCODE?", GenerateGrammarClassCode(ast))
|
---|
150 | .Replace("?IDENT?", ast.Name)
|
---|
151 | .Replace("?FITNESSFUNCTION?", ast.FitnessFunctionNode.SrcCode)
|
---|
152 | .Replace("?INTERPRETERSOURCE?", GenerateInterpreterSource(ast))
|
---|
153 | .Replace("?INITCODE?", ast.InitCodeNode.SrcCode)
|
---|
154 | .Replace("?ADDITIONALCODE?", ast.ClassCodeNode.SrcCode)
|
---|
155 | .Replace("?CONSTRAINTSSOURCE?", GenerateConstraintMethods(ast.Terminals))
|
---|
156 | .Replace("?PATHGENERATORCODE?", GeneratePathGeneratorCode(ast))
|
---|
157 | ;
|
---|
158 |
|
---|
159 | problemSourceCode.AppendLine(problemClassCode).AppendLine();
|
---|
160 | }
|
---|
161 |
|
---|
162 | private string GenerateGrammarClassCode(GPDefNode ast) {
|
---|
163 | var sb = new StringBuilder();
|
---|
164 | // RootSymbol
|
---|
165 | sb.AppendFormat("public static string RootSymbol {{ get {{ return {0}; }} }}", ast.Rules.First().NtSymbol).AppendLine();
|
---|
166 | // GetAlternatives
|
---|
167 | sb.AppendFormat("public static IEnumerable<IEnumerable<string>> GetAlternatives(string symbol) {{");
|
---|
168 | sb.AppendFormat("switch(symbol) {{ ").AppendLine();
|
---|
169 | foreach (var ntSymbol in ast.NonTerminals) {
|
---|
170 | sb.AppendFormat("case {0}: {{ ").AppendLine();
|
---|
171 | var rule = ast.Rules.Single(r => r.NtSymbol == ntSymbol.Ident);
|
---|
172 | sb.AppendFormat("return new string[][] {{}}", rule.RuleExpr);
|
---|
173 | sb.AppendLine("break;}}");
|
---|
174 | }
|
---|
175 | foreach (var tSymbol in ast.NonTerminals) {
|
---|
176 |
|
---|
177 | }
|
---|
178 | sb.AppendFormat(" else {{ throw new InvalidOperationException(\"Unkown symbol: \"+symbol); }}");
|
---|
179 | sb.AppendLine("}}");
|
---|
180 | sb.AppendFormat("}}").AppendLine();
|
---|
181 |
|
---|
182 | // NumberOfAlternatives
|
---|
183 | sb.AppendFormat(
|
---|
184 | "public static int NumberOfAlternatives(string symbol) {{ return GetAlternatives(symbol).Count(); }}").AppendLine();
|
---|
185 | return sb.ToString();
|
---|
186 | }
|
---|
187 |
|
---|
188 | // produces helper methods for the attributes of all terminal nodes
|
---|
189 | private string GenerateConstraintMethods(List<SymbolNode> symbols) {
|
---|
190 | var sb = new StringBuilder();
|
---|
191 | var terminals = symbols.OfType<TerminalNode>();
|
---|
192 | foreach (var t in terminals) {
|
---|
193 | sb.AppendLine(GenerateConstraintMethods(t));
|
---|
194 | }
|
---|
195 | return sb.ToString();
|
---|
196 | }
|
---|
197 |
|
---|
198 | // generates helper methods for the attributes of a given terminal node
|
---|
199 | private string GenerateConstraintMethods(TerminalNode t) {
|
---|
200 | var sb = new StringBuilder();
|
---|
201 | foreach (var c in t.Constraints) {
|
---|
202 | var fieldType = t.FieldDefinitions.First(d => d.Identifier == c.Ident).Type;
|
---|
203 | if (c.Type == ConstraintNodeType.Range) {
|
---|
204 | sb.AppendFormat("public {0} GetMax{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMaxExpression).AppendLine();
|
---|
205 | sb.AppendFormat("public {0} GetMin{1}_{2}() {{ return {3}; }}", fieldType, t.Ident, c.Ident, c.RangeMinExpression).AppendLine();
|
---|
206 | } else if (c.Type == ConstraintNodeType.Set) {
|
---|
207 | sb.AppendFormat("public {0}[] GetAllowed{1}_{2}() {{ return {3}.ToArray(); }}", fieldType, t.Ident, c.Ident, c.SetExpression).AppendLine();
|
---|
208 | }
|
---|
209 | sb.AppendFormat("public {0} Get{1}_{2}Element(int i) {{ return GetAllowed{1}_{2}()[i]; }}", fieldType, t.Ident, c.Ident);
|
---|
210 | }
|
---|
211 | return sb.ToString();
|
---|
212 | }
|
---|
213 |
|
---|
214 |
|
---|
215 | // generates code for a breath-first-search generating all possible paths through the grammar
|
---|
216 | private string GeneratePathGeneratorCode(GPDefNode ast) {
|
---|
217 | var sb = new StringBuilder();
|
---|
218 | foreach (var s in ast.NonTerminals) {
|
---|
219 | sb.Append(GeneratePathGeneratorCode(s));
|
---|
220 | }
|
---|
221 | foreach (var s in ast.Terminals) {
|
---|
222 | sb.Append(GeneratePathGeneratorCode(s));
|
---|
223 | }
|
---|
224 | return sb.ToString();
|
---|
225 | }
|
---|
226 |
|
---|
227 | // generates code for a breath-first-search generating all possible paths through the grammar
|
---|
228 | private string GeneratePathGeneratorCode(SymbolNode s) {
|
---|
229 | var sb = new StringBuilder();
|
---|
230 |
|
---|
231 | return sb.ToString();
|
---|
232 | }
|
---|
233 |
|
---|
234 | private string GenerateInterpreterSource(GPDefNode definition) {
|
---|
235 | var sb = new StringBuilder();
|
---|
236 | // create a grammar instance based on the AST
|
---|
237 | var g = new Grammar(definition.NonTerminals, definition.Terminals, definition.Rules);
|
---|
238 | // find formal parameters of root node
|
---|
239 | string formalParameter = definition.NonTerminals.Single(nt => nt.Ident == g.RootSymbol).FormalParameters;
|
---|
240 | // actual parameter are the same as formalparameter only without type identifier
|
---|
241 | var actualParameterEnumerable =
|
---|
242 | Util.ExtractFormalParameters(formalParameter).Select(e => e.RefOrOut + " " + e.Identifier);
|
---|
243 | string actualParameter = string.Empty;
|
---|
244 | // generate a string of actual parameters beginning with: ', a0, a1, ...'
|
---|
245 | if (actualParameterEnumerable.Any()) {
|
---|
246 | foreach (var e in actualParameterEnumerable) {
|
---|
247 | actualParameter += ", " + e;
|
---|
248 | }
|
---|
249 | }
|
---|
250 | // generate entry method for evaluation. This is called from the min/max function
|
---|
251 | // e.g.: ProgramRoot(ref int a0) { ProgramRoot(rootNode , ref a0); }
|
---|
252 | sb.AppendFormat("void {0}({1}) {{ {0}(currentPath {2}); }}", g.RootSymbol, formalParameter, actualParameter).AppendLine();
|
---|
253 |
|
---|
254 | // generate methods for all nonterminals and terminals using the grammar instance
|
---|
255 | foreach (var s in definition.NonTerminals) {
|
---|
256 | sb.AppendLine(GenerateInterpreterMethod(g, s));
|
---|
257 | }
|
---|
258 | foreach (var s in definition.Terminals) {
|
---|
259 | sb.AppendLine(GenerateTerminalInterpreterMethod((TerminalNode)s));
|
---|
260 | }
|
---|
261 | return sb.ToString();
|
---|
262 | }
|
---|
263 |
|
---|
264 | private string GenerateTerminalInterpreterMethod(TerminalNode s) {
|
---|
265 | var sb = new StringBuilder();
|
---|
266 | // if the terminal symbol has attributes then we must create values for these attributes
|
---|
267 | if (!s.FormalParameters.Any())
|
---|
268 | sb.AppendFormat("private void {0}(IEnumerator<int> path) {{", s.Ident);
|
---|
269 | else
|
---|
270 | sb.AppendFormat("private void {0}(IEnumerator<int> path, {1}) {{", s.Ident, s.FormalParameters);
|
---|
271 |
|
---|
272 | // each field must match a formal parameter, assign a value for each parameter
|
---|
273 | foreach (var element in s.FieldDefinitions) {
|
---|
274 | // read next symbol
|
---|
275 | sb.AppendLine("path.MoveNext();");
|
---|
276 | sb.AppendFormat("{0} = Get{1}_{0}Element(path.Current)", element.Identifier, s.Ident).AppendLine(";");
|
---|
277 | }
|
---|
278 | sb.AppendLine("}");
|
---|
279 | return sb.ToString();
|
---|
280 | }
|
---|
281 |
|
---|
282 | private string GenerateInterpreterMethod(Grammar g, SymbolNode s) {
|
---|
283 | var sb = new StringBuilder();
|
---|
284 | if (!s.FormalParameters.Any())
|
---|
285 | sb.AppendFormat("private void {0}(IEnumerator<int> path) {{", s.Ident);
|
---|
286 | else
|
---|
287 | sb.AppendFormat("private void {0}(IEnumerator<int> path, {1}) {{", s.Ident, s.FormalParameters);
|
---|
288 | // generate local definitions
|
---|
289 | sb.AppendLine(g.GetLocalDefinitions(s.Ident));
|
---|
290 |
|
---|
291 | // read next symbol
|
---|
292 | sb.AppendLine("path.MoveNext();");
|
---|
293 |
|
---|
294 | // if there are alternatives for this symbol -> choose alternative based on the path
|
---|
295 | var alts = g.GetAlternatives(s.Ident);
|
---|
296 | if (alts.Count() > 1) {
|
---|
297 | int i = 0;
|
---|
298 | sb.AppendLine("switch(path.Current) {");
|
---|
299 | // generate a case for each alternative
|
---|
300 | foreach (var l in alts) {
|
---|
301 | sb.AppendFormat("case {0}: {{ ", i).AppendLine();
|
---|
302 | foreach (var e in g.GetSequenceWithSemanticActions(s.Ident, i++)) {
|
---|
303 | sb.AppendLine(GenerateSourceForAction(e));
|
---|
304 | }
|
---|
305 | sb.AppendLine("break;").AppendLine("}");
|
---|
306 | }
|
---|
307 | sb.AppendLine("} else throw new System.InvalidOperationException()").AppendLine();
|
---|
308 | } else {
|
---|
309 | foreach (var e in g.GetSequenceWithSemanticActions(s.Ident, 0)) {
|
---|
310 | sb.AppendLine(GenerateSourceForAction(e));
|
---|
311 | }
|
---|
312 | }
|
---|
313 | sb.AppendLine("}");
|
---|
314 | return sb.ToString();
|
---|
315 | }
|
---|
316 |
|
---|
317 | // helper for generating calls to other symbol methods
|
---|
318 | private string GenerateSourceForAction(RuleExprNode e) {
|
---|
319 | var action = e as RuleActionNode;
|
---|
320 | var call = e as CallSymbolNode;
|
---|
321 | if (action != null) {
|
---|
322 | return action.SrcCode + ";";
|
---|
323 | } else if (call != null) {
|
---|
324 | if (!call.ActualParameter.Any())
|
---|
325 | return string.Format("{0}(path);", call.Ident);
|
---|
326 | else
|
---|
327 | return string.Format("{0}(path, {1});", call.Ident, call.ActualParameter);
|
---|
328 | } else {
|
---|
329 | throw new ArgumentException();
|
---|
330 | }
|
---|
331 | }
|
---|
332 | }
|
---|
333 | }
|
---|