1<#
2//
3// Copyright (c) 2010-2025 Antmicro
4// Copyright (c) 2011-2015 Realtime Embedded
5//
6// This file is licensed under the MIT License.
7// Full license text is available in 'licenses/MIT.txt'.
8//
9#>
10<#@ template language="C#" #>
11<#@ assembly name="System.Core" #>
12<#@ import namespace="System" #>
13<#@ import namespace="System.IO" #>
14<#@ import namespace="System.Linq" #>
15<#@ import namespace="System.Text" #>
16<#@ import namespace="System.Collections.Generic" #>
17<#@ import namespace="System.Text.RegularExpressions" #>
18<#+
19    public class RegistersEnumParser
20    {
21        public RegistersEnumParser(Stream stream) : this(stream, new string[0])
22        {
23        }
24
25        public RegistersEnumParser(Stream stream, IEnumerable<string> defines)
26        {
27            registers = new List<RegisterDescriptor>();
28            registerGroups = new List<RegisterGroupDescriptor>();
29            groupedRegisters = new Dictionary<string, List<Tuple<RegisterDescriptor, int>>>();
30            handlers = new Dictionary<Mode, Action<string>>
31            {
32                { Mode.ScanForEnum,          ScanForEnumHandler          },
33                { Mode.InsideEnum,           InsideEnumHandler           },
34                { Mode.SkipLines,            SkipLinesHandler            },
35                { Mode.IncludeLines,         IncludeLinesHandler         }
36            };
37            modes = new Stack<Mode>();
38
39            this.defines = defines;
40            Parse(stream);
41        }
42
43        public void Map(string from, string to)
44        {
45            var regTo = registers.SingleOrDefault(x => x.Name == to);
46            if(regTo.Name == null)
47            {
48                throw new ArgumentException(string.Format("No register named {0} found.", to));
49            }
50
51            var regFrom = new RegisterDescriptor
52            {
53                Name = from,
54                Width = regTo.Width,
55                Value = regTo.Value
56            };
57
58            registers.Add(regFrom);
59        }
60
61        public void Ignore(string name)
62        {
63            var reg = registers.Cast<RegisterDescriptorBase>().Union(registerGroups.Cast<RegisterDescriptorBase>()).SingleOrDefault(x => x.Name == name);
64            if(reg != null)
65            {
66                reg.IsIgnored = true;
67            }
68        }
69
70        public RegisterDescriptor[] Registers { get { return registers.ToArray(); } }
71        public RegisterGroupDescriptor[] RegisterGroups { get { return registerGroups.ToArray(); } }
72
73        private void Parse(Stream stream)
74        {
75            modes.Push(Mode.ScanForEnum);
76
77            using(var reader = new StreamReader(stream))
78            {
79                string line;
80                while((line = reader.ReadLine()) != null)
81                {
82                    handlers[modes.Peek()](line);
83                }
84            }
85
86            foreach(var group in groupedRegisters)
87            {
88                var widths = group.Value.Select(x => x.Item1.Width).Distinct().ToList();
89                if(widths.Count != 1)
90                {
91                    // we found at least two registers having index with the same name, but different width
92                    throw new ArgumentException(string.Format("Inconsistent register width detected for group: {0}", group.Key));
93                }
94
95                var groupDescriptor = new RegisterGroupDescriptor
96                {
97                    Name = group.Key,
98                    Width = widths.First(),
99                    IndexValueMap = new Dictionary<int, int>()
100                };
101
102                foreach(var x in group.Value.Select(x => Tuple.Create(x.Item2, x.Item1.Value)))
103                {
104                    groupDescriptor.IndexValueMap.Add(x.Item1, x.Item2);
105                }
106
107                registerGroups.Add(groupDescriptor);
108            }
109        }
110
111        private void ScanForEnumHandler(string line)
112        {
113            if(line == BeginningOfEnum)
114            {
115                modes.Push(Mode.InsideEnum);
116            }
117        }
118
119        private void InsideEnumHandler(string line)
120        {
121            // Trim lines with single line comment only
122            line = Regex.Replace(line, @"^(\s*//.*)$", "").Trim();
123            if(line.Length == 0)
124            {
125                return;
126            }
127
128            Mode? newMode = null;
129            if(line.StartsWith(BeginningOfIfdef, StringComparison.CurrentCulture))
130            {
131                var value = line.Replace(BeginningOfIfdef, string.Empty).Trim();
132                newMode = defines.Contains(value) ? Mode.IncludeLines : Mode.SkipLines;
133            }
134            else if(line.StartsWith(BeginningOfIfndef, StringComparison.CurrentCulture))
135            {
136                var value = line.Replace(BeginningOfIfndef, string.Empty).Trim();
137                // Notice the modes are inverted compared to 'ifdef'.
138                newMode = defines.Contains(value) ? Mode.SkipLines : Mode.IncludeLines;
139            }
140
141            if(newMode.HasValue)
142            {
143                modes.Push(newMode.Value);
144                return;
145            }
146
147            if(line == EndOfEnum)
148            {
149                modes.Pop();
150                return;
151            }
152
153            // e.g., R_0_32 = 147,
154            // X_32 = 155,
155            var match = Regex.Match(line, @"^(?<name>[\p{L}0-9]+)(_(?<index>[0-9]+))?_(?<width>[0-9]+)\s*=\s*(?<value>((0x)?[0-9a-fA-F]+)|([0-9]+))\s*,?$");
156            if(string.IsNullOrEmpty(match.Groups["name"].Value))
157            {
158                throw new ArgumentException($"Register name was in a wrong format: {line}");
159            }
160
161            var regValue = match.Groups["value"].Value;
162            var regDesc = new RegisterDescriptor
163            {
164                Name = match.Groups["name"].Value,
165                Width = int.Parse(match.Groups["width"].Value),
166                Value = Convert.ToInt32(regValue, regValue.StartsWith("0x") ? 16 : 10)
167            };
168
169            if(!string.IsNullOrEmpty(match.Groups["index"].Value))
170            {
171                if(!groupedRegisters.ContainsKey(regDesc.Name))
172                {
173                    groupedRegisters[regDesc.Name] = new List<Tuple<RegisterDescriptor, int>>();
174                }
175
176                var index = int.Parse(match.Groups["index"].Value);
177                groupedRegisters[regDesc.Name].Add(Tuple.Create(regDesc, index));
178            }
179            else
180            {
181                registers.Add(regDesc);
182            }
183        }
184
185        private void IncludeLinesHandler(string line)
186        {
187            if(line.Trim() == EndOfIfdef)
188            {
189                modes.Pop();
190            }
191            else if(line.Trim() == ElseIfdef)
192            {
193                modes.Pop();
194                modes.Push(Mode.SkipLines);
195            }
196            else
197            {
198                InsideEnumHandler(line);
199            }
200        }
201
202        private void SkipLinesHandler(string line)
203        {
204            if(line.Trim() == EndOfIfdef)
205            {
206                modes.Pop();
207            }
208            else if(line.Trim() == ElseIfdef)
209            {
210                modes.Pop();
211                modes.Push(Mode.IncludeLines);
212            }
213            else if(line.Trim().StartsWith("#if"))
214            {
215                // The mode should still be 'SkipLines' after ifdef + endif pairs.
216                modes.Push(Mode.SkipLines);
217            }
218        }
219
220        private readonly List<RegisterDescriptor> registers;
221        private readonly List<RegisterGroupDescriptor> registerGroups;
222
223        private readonly IEnumerable<string> defines;
224
225        private readonly Dictionary<Mode, Action<string>> handlers;
226        private readonly Stack<Mode> modes;
227        private readonly Dictionary<string, List<Tuple<RegisterDescriptor, int>>> groupedRegisters;
228
229        private const string BeginningOfIfdef = "#ifdef";
230        private const string BeginningOfIfndef = "#ifndef";
231        private const string ElseIfdef = "#else";
232        private const string EndOfIfdef = "#endif";
233        private const string BeginningOfEnum = "typedef enum {";
234        private const string EndOfEnum = "} Registers;";
235
236        private enum Mode
237        {
238            ScanForEnum,
239            InsideEnum,
240            SkipLines,
241            IncludeLines
242        }
243
244        public class RegisterDescriptor : RegisterDescriptorBase
245        {
246            public int Value { get; set; }
247        }
248
249        public class RegisterGroupDescriptor : RegisterDescriptorBase
250        {
251            public Dictionary<int, int> IndexValueMap { get; set; }
252
253            public IEnumerable<RegisterDescriptor> GetRegisters()
254            {
255                return IndexValueMap.Select(x => new RegisterDescriptor
256                {
257                    Name = $"{this.Name}{x.Key}",
258                    Width = this.Width,
259                    IsIgnored = this.IsIgnored,
260                    Value = x.Value
261                });
262            }
263        }
264
265        public class RegisterDescriptorBase
266        {
267            public string Name { get; set; }
268            public int Width { get; set; }
269            public bool IsIgnored { get; set; }
270        }
271    }
272
273    public static class RegisterTypeHelper
274    {
275        public static string GetTypeName(int width)
276        {
277            switch(width)
278            {
279            case 64:
280                return "ulong";
281            case 32:
282                return "uint";
283            case 16:
284                return "ushort";
285            case 8:
286                return "byte";
287            default:
288                throw new ArgumentException("Width not supported");
289            }
290        }
291    }
292#>
293