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