// reads a CANUN path private string ReadCanunPath(StonTokenReader reader) { StringBuilder builder = new StringBuilder(); reader.ReadCanun(builder); while (reader.Peek().HasChartype(StonChartype.NameSeparator)) { builder.Append((char)reader.ReadAndSkip()); reader.ReadCanun(builder); } return(builder.ToString()); }
// reads a binding key private IStonBindingName ReadBindingName(StonTokenReader reader) { string name = null; bool isExtension = false; if (reader.TryAndSkip(StonChartype.Extension)) { isExtension = true; } if (reader.Peek().HasChartype(StonChartype.TextDelimiter)) { name = reader.ReadString(); } else if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { name = reader.ReadCanun(); } else { throw reader.MakeUnexpectedCharacterException(StonChartype.TextDelimiter | StonChartype.CanunBegin); } return(ElementFactory.CreateBindingName(name, isExtension)); }
// reads a STON address initial context private IStonInitialContext ReadInitialContext(StonTokenReader reader) { int addressOpen = reader.ExpectChartype(StonChartype.AddressBegin); // reference defining context if (addressOpen == '$') { reader.PeekSignificant(); return(ElementFactory.CreateAncestorInitialContext(0)); } else if (addressOpen == '^') { // document core if (reader.Peek() == '*') { reader.ReadAndSkip(); return(ElementFactory.CreateGlobalEntityInitialContext("")); } // reference defining context ancestor else { int ancestorOrder = 1; reader.PeekSignificant(); while (reader.TryAndSkip(StonChartype.AncestorAccess)) { ++ancestorOrder; } return(ElementFactory.CreateAncestorInitialContext(ancestorOrder)); } } // globally identified entity else { reader.PeekSignificant(); string globalIdentifier = reader.ReadCanun(); return(ElementFactory.CreateGlobalEntityInitialContext(globalIdentifier)); } }
// reads a construction component of a complex value private IStonConstruction ReadConstruction(StonTokenReader reader) { reader.ExpectAndSkip(StonChartype.ConstructionOpen); var positionalParameters = new List <IStonEntity>(); var namedParameters = new List <KeyValuePair <string, IStonEntity> >(); while (!reader.TryAndSkip(StonChartype.ConstructionClose)) { // trying to read a positional parameter, or the first named parameter if (namedParameters.Count == 0) { // value prompt right at the beginning clearly indicates a positional parameter if (reader.TryAndSkip(StonChartype.ValuePrompt)) { positionalParameters.Add(ReadEntity(reader)); } // string literal might be an entity or a named parameter name else if (reader.Peek().HasChartype(StonChartype.TextDelimiter)) { var builder = new StringBuilder(); reader.ReadString(builder, false); // if it's a single string literal followed by a value prompt, it means it is the first named parameter if (reader.TryAndSkip(StonChartype.ValuePrompt)) { namedParameters.Add(new KeyValuePair <string, IStonEntity>(builder.ToString(), ReadEntity(reader))); } // if no value prompt follows, it is assumed to be a string-valued entity else { while (reader.Peek().HasChartype(StonChartype.StringChainContinue)) { if (reader.ReadAndSkip().HasChartype(StonChartype.StringChainOpen)) { builder.Append('\n'); } reader.ReadString(builder, false); } positionalParameters.Add(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Text, builder.ToString()))); } } else { var entity = ReadEntity(reader); if (reader.Peek().HasChartype(StonChartype.ValuePrompt)) { // if value prompt follows after the read entity, it means the read entity must be a typeless, non-identified CANUN named value // serving as the parameter name if (!(entity is IStonSimpleEntity)) { throw reader.MakeUnexpectedCharacterException(StonChartype.SequenceSeparator | StonChartype.ConstructionClose); } var simpleEntity = entity as IStonSimpleEntity; if (simpleEntity.GlobalIdentifier != null || simpleEntity.Type != null || simpleEntity.Value.DataType != StonDataType.Named || simpleEntity.Value.Content.Contains('.')) { throw reader.MakeUnexpectedCharacterException(StonChartype.SequenceSeparator | StonChartype.ConstructionClose); } reader.ReadAndSkip(); namedParameters.Add(new KeyValuePair <string, IStonEntity>(simpleEntity.Value.Content, ReadEntity(reader))); } else { positionalParameters.Add(entity); } } } // reading remaining named parameters else { string name; if (reader.Peek().HasChartype(StonChartype.TextDelimiter)) { name = reader.ReadString(); } else if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { name = reader.ReadCanun(); } else { throw reader.MakeUnexpectedCharacterException(StonChartype.TextDelimiter | StonChartype.CanunBegin); } reader.ExpectAndSkip(StonChartype.ValuePrompt); IStonEntity value = ReadEntity(reader); namedParameters.Add(new KeyValuePair <string, IStonEntity>(name, value)); } if (reader.TryAndSkip(StonChartype.SequenceSeparator)) { continue; } else if (!reader.Peek().HasChartype(StonChartype.ConstructionClose)) { throw reader.MakeUnexpectedCharacterException(StonChartype.SequenceSeparator | StonChartype.ConstructionClose); } } return(ElementFactory.CreateConstruction(positionalParameters, namedParameters)); }
// reads a STON entity, complete or address private IStonEntity _ReadEntity(StonTokenReader reader, bool isAddress) { // stores a CANUN identifier or path for later use until its purpose is known string storedWord = null; // determining the global identifier, if any string globalIdentifier = null; // the identifier is explicitly prepended with a global identifier sigil if (reader.Peek().HasChartype(StonChartype.IdentifierSigil)) { if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // identifiers are not allowed for index parameter entities } reader.ReadAndSkip(); globalIdentifier = reader.ReadCanun(); reader.ExpectAndSkip(StonChartype.IdentifierAssign); } // the entity begins with a CANUN name that might or might not be a global identifier else if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { storedWord = reader.ReadCanun(); // if and only if the CANUN name is followed by a global identifier assignment token // the name is to be used as a global identifier if (reader.Peek().HasChartype(StonChartype.IdentifierAssign)) { if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // once again, identifiers are not allowed for index parameter entities } reader.ReadAndSkip(); globalIdentifier = storedWord; storedWord = null; } } // finding the address, if any if (reader.Peek().HasChartype(StonChartype.AddressBegin)) { // the reference entity cannot have a type declaration if (storedWord != null) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); } return(ElementFactory.CreateReferenceEntity(ReadAddress(reader), globalIdentifier)); } // determining the type, if any IStonType type = null; bool isTypeFromWord = false; bool collectionStarted = false; // a variable indicating whether during reading type a collection initialization has been started or not // when bare type definitions are involved, a collection initialization might appear like a collection type suffix // so it's impossible to tell the two apart before reading the initial sign // no word is stored; either there was no word in the first place, or there was a global identifier before if (storedWord == null) { // storing a CANUN path, which might be a type or a named value if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { storedWord = ReadCanunPath(reader); } // reading a type if the following token is characteristic for types else if (reader.Peek().HasChartype(StonChartype.Extension | StonChartype.TypeOpen)) { type = ReadBareUnion(reader, null, ref collectionStarted); } } else { // if a CANUN name is stored, it might be a part of a longer CANUN path // which itself might be a type or a named value if (reader.Peek().HasChartype(StonChartype.NameSeparator)) { storedWord = storedWord + (char)reader.ReadAndSkip() + ReadCanunPath(reader); } } // if a CANUN path is followed by a type opening, collection suffix or a union type separator // it means that path must be a type name if (storedWord != null && reader.Peek().HasChartype(StonChartype.TypeOpen | StonChartype.CollectionSuffixBegin | StonChartype.UnionTypeSeparator)) { type = ReadBareUnion(reader, storedWord, ref collectionStarted); storedWord = null; } if (storedWord != null) { type = ElementFactory.CreateNamedType(storedWord); isTypeFromWord = true; } // finding the complex value, if any // when the collection initialization has been entered when reading a type definition // the rest of the collection initialization is read if (collectionStarted) { // a complex value is not allowed in an address index parameter if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.CollectionSuffixContinue); } // reading the rest of the collection initialization // and subsequent member initialization, if available IStonCollectionInit collectionInit = ReadCollectionInit(reader, true); IStonMemberInit memberInit = null; if (reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } return(ElementFactory.CreateComplexEntity(null, memberInit, collectionInit, type, globalIdentifier)); } // reading any valid combination of complex value components (construction, member initialization, collection initialization) if (reader.Peek().HasChartype(StonChartype.ConstructionOpen | StonChartype.MemberInitOpen | StonChartype.CollectionInitOpen)) { // a complex value is not allowed in an address index parameter if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); } IStonConstruction constructor = null; IStonMemberInit memberInit = null; IStonCollectionInit collectionInit = null; if (reader.Peek().HasChartype(StonChartype.ConstructionOpen)) { constructor = ReadConstruction(reader); } if (reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } if (reader.Peek().HasChartype(StonChartype.CollectionInitOpen)) { collectionInit = ReadCollectionInit(reader); if (memberInit == null && reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } } return(ElementFactory.CreateComplexEntity(constructor, memberInit, collectionInit, type, globalIdentifier)); } // finding the string, binary or number simple value, if complex value is not present // reading a string literal or string literals chain if (reader.TryAndSkip(StonChartype.StringChainOpen)) { if (!reader.Peek().HasChartype(StonChartype.TextDelimiter | StonChartype.CodeDelimiter)) { throw reader.MakeUnexpectedCharacterException(StonChartype.TextDelimiter | StonChartype.CodeDelimiter); } } if (reader.Peek().HasChartype(StonChartype.TextDelimiter | StonChartype.CodeDelimiter)) { bool isCode = reader.Peek().HasChartype(StonChartype.CodeDelimiter); // whether the value is a text or a code var builder = new StringBuilder(); reader.ReadString(builder, isCode); while (reader.Peek().HasChartype(StonChartype.StringChainContinue)) { if (reader.ReadAndSkip().HasChartype(StonChartype.StringChainOpen)) { builder.Append('\n'); } reader.ReadString(builder, isCode); } return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(isCode ? StonDataType.Code : StonDataType.Text, builder.ToString()), type, globalIdentifier)); } // reading a number of binary value bool minus = false; // reading the initial sign, if any if (reader.Peek().HasChartype(StonChartype.Sign)) { if (reader.ReadAndSkip() == '-') { minus = true; } // a digit must follow the sign if (!reader.Peek().HasChartype(StonChartype.Digit)) { throw reader.MakeUnexpectedCharacterException(StonChartype.Digit); } } // both number and binary literals might begin with a zero (excluding initial sign) // whichever it is, is decided by whether the zero is followed by a base identifier or not if (reader.TryAndSkip(StonChartype.ZeroDigit)) { if (reader.Peek().HasChartype(StonChartype.BaseIdentifier)) { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Binary, reader.ReadBinaryContent(minus)), type, globalIdentifier)); } else { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Number, reader.ReadNumberContent(minus)), type, globalIdentifier)); } } // only number literals might begin with a non-zero digit (aside from initial sign) else if (reader.Peek().HasChartype(StonChartype.NonZeroDigit)) { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Number, reader.ReadNumberContent(minus)), type, globalIdentifier)); } // finding the named or null value, if everything else fails string value; if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { value = ReadCanunPath(reader); } else if (isTypeFromWord) { value = (type as IStonNamedType).Name; type = null; } else { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // there is no named value, and thus there's all the reason to panic } if (value == "null") { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Null, null), type, globalIdentifier)); } else { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Named, value), type, globalIdentifier)); } }