1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Sprache;
11 
12 // all elements of parse tree has to be located in this namespace
13 // because SyntaxTreeHelper only visits objects of types located there
14 namespace Antmicro.Renode.PlatformDescription.Syntax
15 {
16     public static class Grammar
17     {
18         // note that all fields here are created in the file order, i.e. as written
19         // due to that, depending on a field that is below can lead to a unexpected NullReferenceException
20         // in such case use Parsers.Ref <-- see Sprache's xml docs
21 
22         public static readonly HashSet<string> Keywords = new HashSet<string>();
23 
24         public static readonly Parser<char> Separator = Parse.Char(';').Token();
25 
26         public static readonly Parser<char> AtSign = Parse.Char('@').Token();
27 
28         public static readonly Parser<char> MultiplexSign = Parse.Char('|').Token();
29 
30         public static readonly Parser<char> Colon = Parse.Char(':').Token().Named("colon");
31 
32         public static readonly Parser<char> Comma = Parse.Char(',').Token();
33 
34         public static readonly Parser<char> PlusSign = Parse.Char('+').Token();
35 
36         public static readonly Parser<char> OpeningChevron = Parse.Char('<').Token();
37 
38         public static readonly Parser<char> ClosingChevron = Parse.Char('>').Token();
39 
40         public static readonly Parser<char> OpeningBrace = Parse.Char('{').Token();
41 
42         public static readonly Parser<char> ClosingBrace = Parse.Char('}').Token();
43 
44         public static readonly Parser<char> OpeningSquareBracket = Parse.Char('[').Token();
45 
46         public static readonly Parser<char> ClosingSquareBracket = Parse.Char(']').Token();
47 
48         public static readonly Parser<char> Minus = Parse.Char('-').Token();
49 
50         //not set as a token, as it may be used inside strings where we want to preserve spaces
51         public static readonly Parser<char> QuotationMark = Parse.Char('"');
52 
53         public static readonly Parser<string> MultiQuotationMark = Parse.String("'''").Text();
54 
55         public static readonly Parser<char> EscapeCharacter = Parse.Char('\\');
56 
57         public static readonly Parser<char> NumberSeparator = Parse.Char('_');
58 
59         public static readonly Parser<string> RightArrow = Parse.String("->").Text().Token().Named("arrow");
60 
61         public static readonly Parser<string> HexadecimalPrefix = Parse.String("0x").Text();
62 
63         public static readonly Parser<string> DigitSequence =
64             (from digits in Parse.Number
65              from digitsContinuation in NumberSeparator.Then(_ => Parse.Number).Many().Select(x => String.Join(String.Empty, x))
66              select digits + digitsContinuation);
67 
68         public static readonly Parser<long> DecimalLong = DigitSequence.Select(x => long.Parse(x)).Token().Named("decimal number");
69 
70         public static readonly Parser<ulong> DecimalUnsignedLong = DigitSequence.Select(x => ulong.Parse(x)).Token().Named("decimal non-negative number");
71 
72         public static readonly Parser<char> HexadecimalDigit = Parse.Chars("0123456789ABCDEFabcdef");
73 
74         public static readonly Parser<string> HexadecimalDigitSequence =
75             (from hexadecimalDigits in HexadecimalDigit.AtLeastOnce().Text()
76              from hexadecimalDigitsContinuation in NumberSeparator.Then(_ => HexadecimalDigit.AtLeastOnce().Text()).Many().Select(x => String.Join(String.Empty, x))
77              select hexadecimalDigits + hexadecimalDigitsContinuation);
78 
79         public static readonly Parser<ulong> HexadecimalUnsignedLong =
80             HexadecimalPrefix.Then(x => HexadecimalDigitSequence.Select(y => ulong.Parse(y, System.Globalization.NumberStyles.HexNumber))).Token().Named("hexadecimal number");
81 
82         public static readonly Parser<uint> HexadecimalUnsignedInt = HexadecimalUnsignedLong.Select(x => (uint)x);
83         public static readonly Parser<int> HexadecimalInt = HexadecimalUnsignedLong.Select(x => (int)x);
84 
85         public static readonly Parser<int> DecimalInt = DecimalLong.Select(x => (int)x);
86         public static readonly Parser<uint> DecimalUnsignedInt = DecimalUnsignedLong.Select(x => (uint)x);
87 
88         public static readonly Parser<string> HexadecimalNumberWithSign =
89             (from sign in Minus.Optional()
90              from prefix in HexadecimalPrefix
91              from digits in HexadecimalDigitSequence
92              select (sign.IsDefined ? "-" : "") + prefix + digits).Token().Named("hexadecimal number");
93 
94         public static readonly Parser<string> DecimalNumberWithSign =
95             (from sign in Minus.Optional()
96              from integerPart in DigitSequence
97              from fractionalPart in (Parse.Char('.').Then(x => Parse.Number.Select(y => x + y))).Optional()
98              select (sign.IsDefined ? "-" : "") + integerPart + fractionalPart.GetOrElse("")).Token().Named("decimal number");
99 
100         public static readonly Parser<string> Number = HexadecimalNumberWithSign.Or(DecimalNumberWithSign);
101 
102         public static readonly Parser<string> GeneralIdentifier =
103             (from startingLetter in Parse.Letter.Once().Text()
104              from rest in Parse.LetterOrDigit.Or(Parse.Char('_')).Many().Text()
105              select startingLetter + rest).Token();
106 
107         public static readonly Parser<string> UsingKeyword = MakeKeyword("using");
108 
109         public static readonly Parser<string> NewKeyword = MakeKeyword("new");
110 
111         public static readonly Parser<string> AsKeyword = MakeKeyword("as");
112 
113         public static readonly Parser<string> InitKeyword = MakeKeyword("init");
114 
115         public static readonly Parser<string> LocalKeyword = MakeKeyword("local");
116 
117         public static readonly Parser<string> PrefixedKeyword = MakeKeyword("prefixed");
118 
119         public static readonly Parser<bool> TrueKeyword = MakeKeyword("true", true);
120 
121         public static readonly Parser<bool> FalseKeyword = MakeKeyword("false", false);
122 
123         public static readonly Parser<Value> NoneKeyword = MakeKeyword("none", (Value)null);
124 
125         public static readonly Parser<EmptyValue> EmptyKeyword = MakeKeyword("empty", EmptyValue.Instance);
126 
127         public static readonly Parser<string> Identifier = GeneralIdentifier.Named("identifier").Where(x => !Keywords.Contains(x));
128 
129         public static readonly Parser<StringWithPosition> TypeName =
130             (from first in Identifier
131              from rest in Parse.Char('.').Then(x => Identifier).XMany()
132              select new StringWithPosition(rest.Aggregate(first, (x, y) => x + '.' + y))).Positioned();
133 
134         public static readonly Parser<char> QuotedStringElement = EscapeCharacter.Then(x => QuotationMark.XOr(EscapeCharacter)).XOr(Parse.CharExcept('"'));
135 
136         public static readonly Parser<string> MultiQuotedStringElement = EscapeCharacter.Then(x => MultiQuotationMark).XOr(Parse.AnyChar.Except(MultiQuotationMark).Select(x => x.ToString()));
137 
138         public static readonly Parser<string> SingleLineQuotedString =
139             (from openingQuote in QuotationMark
140              from content in QuotedStringElement.Many().Text()
141              from closingQuote in QuotationMark
142              select content).Token().Named("quoted string");
143 
144         public static readonly Parser<string> MultilineQuotedString =
145             (from openingQuote in MultiQuotationMark
146              from content in MultiQuotedStringElement.Many().Select(x => string.Join(String.Empty, x))
147              from closingQuote in MultiQuotationMark
148              select content).Token().Named("multiline quoted string");
149 
150         public static readonly Parser<UsingEntry> Using =
151             (from usingKeyword in UsingKeyword
152              from filePath in SingleLineQuotedString.Select(x => new StringWithPosition(x)).Positioned()
153              from prefixedKeyword in PrefixedKeyword.Then(x => SingleLineQuotedString).Named("using prefix").Optional()
154              select new UsingEntry(filePath, prefixedKeyword.GetOrDefault())).Token().Positioned().Named("using entry");
155 
156         public static readonly Parser<IEnumerable<UsingEntry>> Usings =
157             (from firstUsing in Using
158              from rest in Separator.Then(x => Using).Many()
159              select new[] { firstUsing }.Concat(rest));
160 
161         public static readonly Parser<RangeValue> Range =
162             (from opening in OpeningChevron
163              from begin in HexadecimalUnsignedLong.Or(DecimalUnsignedLong)
164              from comma in Comma
165              from plus in PlusSign.Optional()
166              from end in HexadecimalUnsignedLong.Or(DecimalUnsignedLong).Select(x => plus.IsEmpty ? x : begin + x).Where(x => begin <= x).Named("end of range that defines positive range")
167              from closing in ClosingChevron
168              select new RangeValue(begin, end)).Named("range");
169 
170         public static readonly Parser<EnumValue> Enum =
171             (from firstElement in Identifier
172              from rest in Parse.Char('.').Then(x => Identifier).XAtLeastOnce()
173              select new EnumValue(new[] { firstElement }.Concat(rest))).Named("enum");
174 
175         public static readonly Parser<ObjectValue> ObjectValue =
176             (from newKeyword in NewKeyword
177              from typeName in TypeName.Named("type name")
178              from attributes in Parse.Ref(() => Attributes).XOptional()
179              select new ObjectValue(typeName, attributes.GetOrElse(new Attribute[0]))).Named("inline object");
180 
181         public static readonly Parser<ReferenceValue> ReferenceValue = Identifier.Select(x => new ReferenceValue(x)).Named("reference");
182 
183         public static readonly Parser<BoolValue> BoolValue =
184             TrueKeyword.Or(FalseKeyword).Select(x => new BoolValue(x)).Named("bool");
185 
186         public static readonly Parser<Value> Value = (SingleLineQuotedString.Or(MultilineQuotedString)).Select(x => new StringValue(x))
187                                                                  .XOr<Value>(Range)
188                                                                  .XOr(Number.Select(x => new NumericalValue(x)))
189                                                                  .XOr(ObjectValue)
190                                                                  .Or(Enum)
191                                                                  .Or(BoolValue)
192                                                                  .Or(ReferenceValue)
193                                                                  .Positioned();
194 
195         public static readonly Parser<RegistrationInfo> RegistrationInfoInner =
196             (from register in ReferenceValue.Named("register reference").Positioned()
197              from registrationPoint in Value.XOptional().Named("registration point")
198              select new RegistrationInfo(register, registrationPoint.GetOrDefault())).Named("registration info");
199 
200         public static readonly Parser<RegistrationInfo> RegistrationInfo = AtSign.Then(x => RegistrationInfoInner);
201 
202         public static readonly Parser<RegistrationInfo> NoneRegistrationInfo =
203             (from atSign in AtSign
204              from noneKeyword in NoneKeyword
205              select new RegistrationInfo(null, null)).Named("none registration info");
206 
207         public static readonly Parser<IEnumerable<RegistrationInfo>> RegistrationInfos =
208             (from atSign in AtSign
209              from brace in OpeningBrace.Named("registration infos list")
210              from first in RegistrationInfoInner
211              from rest in Separator.Then(x => RegistrationInfoInner).XMany()
212              from closingBrace in ClosingBrace.Named("registration infos list end")
213              select new[] { first }.Concat(rest));
214 
215         public static readonly Parser<ConstructorOrPropertyAttribute> ConstructorOrPropertyAttribute =
216             (from identifier in Identifier.Named("constructor or property name")
217              from colon in Colon
218              from value in Value.Named("constructor or property value").Or(NoneKeyword).Or(EmptyKeyword)
219              select new ConstructorOrPropertyAttribute(identifier, value)).Named("constructor or property name and value");
220 
221         public static readonly Parser<string> QuotedMonitorStatementElement =
222             (from openingQuote in QuotationMark
223              from content in QuotedStringElement.Many().Text()
224              from closingQuote in QuotationMark
225              select openingQuote + content + closingQuote).Named("quoted monitor statement element");
226 
227         public static readonly Parser<string> MonitorStatementElement = Parse.AnyChar.Except(Separator.Or(OpeningBrace).Or(ClosingBrace).Or(QuotationMark)).XMany()
228                                                                              .Or(QuotedMonitorStatementElement).Text().Named("monitor statement element");
229 
230         public static readonly Parser<string> MonitorStatement =
231             (from elements in MonitorStatementElement.Many()
232              select elements.Aggregate((x, y) => x + y)).Token().Named("monitor statement");
233 
234         public static readonly Parser<IEnumerable<string>> InitValue =
235             (from openingBrace in OpeningBrace.Named("init statement list")
236              from firstLine in MonitorStatement
237              from rest in Separator.Then(x => MonitorStatement).XMany()
238              from closingBrace in ClosingBrace.Named("init statement list end")
239              select new[] { firstLine }.Concat(rest));
240 
241         public static readonly Parser<InitAttribute> InitAttribute =
242             (from initKeyword in InitKeyword
243              from addSuffix in Identifier.Where(x => x == "add").Optional()
244              from colon in Colon
245              from initValue in InitValue
246              select new InitAttribute(initValue, !addSuffix.IsEmpty)).Named("init section");
247 
248         public static readonly Parser<IEnumerable<int>> IrqRange =
249             (from leftSide in HexadecimalUnsignedInt.Or(DecimalUnsignedInt)
250              from dash in Minus
251              from rightSide in HexadecimalUnsignedInt.Or(DecimalUnsignedInt).Where(x => x != leftSide).Named(string.Format("number other than {0}", leftSide))
252              select MakeSimpleRange(checked((int)leftSide), checked((int)rightSide)));
253 
GetIrqEnd(bool source)254         public static Parser<SingleOrMultiIrqEnd> GetIrqEnd(bool source)
255         {
256             var result = IrqRange.Select(x => new SingleOrMultiIrqEnd(x.Select(y => new IrqEnd(null, y))))
257                                  .Or(HexadecimalInt.Or(DecimalInt).Select(x => new SingleOrMultiIrqEnd(new[] { new IrqEnd(null, x) })));
258             if(source)
259             {
260                 result = result.Or(Identifier.Select(x => new SingleOrMultiIrqEnd(new[] { new IrqEnd(x, 0) })));
261             }
262             return result.Positioned();
263         }
264 
GetIrqEnds(bool source)265         public static Parser<IEnumerable<SingleOrMultiIrqEnd>> GetIrqEnds(bool source)
266         {
267             return
268                 (from openingBracket in OpeningSquareBracket
269                  from first in GetIrqEnd(source)
270                  from rest in Comma.Then(x => GetIrqEnd(source)).XMany()
271                  from closingBracket in ClosingSquareBracket
272                  select new[] { first }.Concat(rest));
273         }
274 
275         public static Parser<IrqReceiver> IrqReceiver =
276             (from destinationName in ReferenceValue.Named("destination peripheral reference")
277              from localIndex in Parse.Char('#').Then(x => Parse.Number.Select(y => (int?)int.Parse(y))).Named("local index").XOptional()
278              select new IrqReceiver(destinationName, localIndex.GetOrDefault())).Positioned();
279 
280         public static readonly Parser<IrqDestinations> SimpleDestination =
281            (from destinationIdentifier in IrqReceiver
282             from at in AtSign
283             from end in GetIrqEnd(false)
284             select new IrqDestinations(destinationIdentifier, new SingleOrMultiIrqEnd[] { end }));
285 
286         public static readonly Parser<IrqDestinations> MultiDestination =
287            (from destinationIdentifier in IrqReceiver
288             from at in AtSign
289             from ends in GetIrqEnds(false)
290             select new IrqDestinations(destinationIdentifier, ends));
291 
292         public static readonly Parser<IrqAttribute> SimpleIrqAttribute =
293             (from source in GetIrqEnd(true).Select(x => new[] { x }).Optional()
294              from arrow in RightArrow
295              from destination in SimpleDestination
296              from rest in MultiplexSign.XOptional().Then(x => SimpleDestination).XMany()
297              select new IrqAttribute(source.GetOrDefault(), new[] { destination }.Concat(rest)));
298 
299         public static readonly Parser<IrqAttribute> MultiIrqAttribute =
300             (from sources in GetIrqEnds(true)
301              from arrow in RightArrow
302              from destination in MultiDestination
303              from rest in MultiplexSign.XOptional().Then(x => MultiDestination).XMany()
304              select new IrqAttribute(sources, new[] { destination }.Concat(rest)));
305 
306         public static readonly Parser<IrqAttribute> NoneIrqAttribute =
307             (from source in GetIrqEnd(true).Select(x => new[] { x }).Optional()
308              from arrow in RightArrow
309              from noneKeyword in NoneKeyword
310              select new IrqAttribute(source.GetOrDefault(), new[] { new IrqDestinations(null, null) }));
311 
312         public static readonly Parser<Attribute> Attribute = InitAttribute
313             .Or<Attribute>(ConstructorOrPropertyAttribute)
314             .Or(NoneIrqAttribute)
315             .Or(SimpleIrqAttribute)
316             .Or(MultiIrqAttribute)
317             .Positioned().Named("attribute");
318 
319         public static readonly Parser<IEnumerable<Attribute>> AttributesInner =
320             (from firstAttribute in Attribute
321              from rest in Separator.Then(x => Attribute).XMany()
322              select new[] { firstAttribute }.Concat(rest));
323 
324         public static readonly Parser<IEnumerable<Attribute>> Attributes =
325             (from openingBrace in OpeningBrace.Named("attribute list")
326              from attributes in AttributesInner.XOptional()
327              from closingBrace in ClosingBrace.Named("attribute list end")
328              select attributes.GetOrElse(new Attribute[0]));
329 
330         public static readonly Parser<Entry> Entry =
331             (from localKeyword in LocalKeyword.Optional()
332              from variableName in Identifier.Named("variable name")
333              from colon in Colon
334              from type in TypeName.XOptional().Named("type name")
335              from registationInfo in RegistrationInfo.Or(NoneRegistrationInfo).Select(x => new[] { x }).Or(RegistrationInfos).XOptional()
336              from alias in AsKeyword.Then(x => SingleLineQuotedString.Select(y => new StringWithPosition(y)).Named("alias").Positioned()).XOptional()
337              from attributes in Attributes.XOptional()
338              select new Entry(variableName, type.GetOrDefault(), registationInfo.GetOrDefault(), attributes.GetOrElse(new Attribute[0]), localKeyword.IsDefined, alias.GetOrDefault()))
339                 .Positioned().Token().Named("entry");
340 
341         public static readonly Parser<IEnumerable<Entry>> Entries =
342             (from firstEntry in Entry
343              from rest in Separator.Then(x => Entry).XMany()
344              select new[] { firstEntry }.Concat(rest));
345 
346         public static readonly Parser<Description> Description =
347             (from whitespace in Parse.WhiteSpace.Many() // we have to consume all whitespaces before deciding, because X decides on first char
348              from usings in Usings.XOptional()
349              from separator in Separator.Optional() // leftover separator from the last using before first entry; user cannot insert it, because it is not added by prelexer
350              from entries in Entries.XOptional()
351              select new Description(usings.GetOrElse(new UsingEntry[0]), entries.GetOrElse(new Entry[0]))).End();
352 
XOptional(this Parser<T> parser)353         public static Parser<IOption<T>> XOptional<T>(this Parser<T> parser)
354         {
355             if(parser == null) throw new ArgumentNullException(nameof(parser));
356 
357             return i =>
358             {
359                 var pr = parser(i);
360 
361                 if(pr.WasSuccessful)
362                     return Result.Success(new Some<T>(pr.Value), pr.Remainder);
363 
364                 if(!pr.Remainder.Equals(i))
365                 {
366                     return Result.Failure<IOption<T>>(pr.Remainder, pr.Message, pr.Expectations);
367                 }
368 
369                 return Result.Success(new None<T>(), i);
370             };
371         }
372 
373         internal abstract class AbstractOption<T> : IOption<T>
374         {
375             public abstract bool IsEmpty { get; }
376 
377             public bool IsDefined
378             {
379                 get { return !IsEmpty; }
380             }
381 
GetOrDefault()382             public T GetOrDefault()
383             {
384                 return IsEmpty ? default(T) : Get();
385             }
386 
Get()387             public abstract T Get();
388         }
389 
390         internal sealed class Some<T> : AbstractOption<T>
391         {
392             private readonly T _value;
393 
Some(T value)394             public Some(T value)
395             {
396                 _value = value;
397             }
398 
399             public override bool IsEmpty
400             {
401                 get { return false; }
402             }
403 
Get()404             public override T Get()
405             {
406                 return _value;
407             }
408         }
409 
410         internal sealed class None<T> : AbstractOption<T>
411         {
412             public override bool IsEmpty
413             {
414                 get { return true; }
415             }
416 
Get()417             public override T Get()
418             {
419                 throw new InvalidOperationException("Cannot get value from None.");
420             }
421         }
422 
MakeKeyword(string keyword)423         public static Parser<string> MakeKeyword(string keyword)
424         {
425             Keywords.Add(keyword);
426             return GeneralIdentifier.Where(x => x == keyword).Named(keyword + " keyword");
427         }
428 
MakeKeyword(string keyword, T obj)429         public static Parser<T> MakeKeyword<T>(string keyword, T obj)
430         {
431             Keywords.Add(keyword);
432             return GeneralIdentifier.Where(x => x == keyword).Named(keyword + " keyword").Select(x => obj);
433         }
434 
MakeSimpleRange(int begin, int end)435         public static IEnumerable<int> MakeSimpleRange(int begin, int end)
436         {
437             if(end >= begin)
438             {
439                 return Enumerable.Range(begin, end - begin + 1);
440             }
441             return Enumerable.Range(end, begin - end + 1).Reverse();
442         }
443     }
444 }
445