mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
232 lines
7.1 KiB
C#
232 lines
7.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Ink.Parsed;
|
|
|
|
namespace Ink
|
|
{
|
|
public partial class InkParser
|
|
{
|
|
protected Sequence InnerSequence()
|
|
{
|
|
Whitespace ();
|
|
|
|
// Default sequence type
|
|
SequenceType seqType = SequenceType.Stopping;
|
|
|
|
// Optional explicit sequence type
|
|
SequenceType? parsedSeqType = (SequenceType?) Parse(SequenceTypeAnnotation);
|
|
if (parsedSeqType != null)
|
|
seqType = parsedSeqType.Value;
|
|
|
|
var contentLists = Parse(InnerSequenceObjects);
|
|
if (contentLists == null || contentLists.Count <= 1) {
|
|
return null;
|
|
}
|
|
|
|
return new Sequence (contentLists, seqType);
|
|
}
|
|
|
|
protected object SequenceTypeAnnotation()
|
|
{
|
|
var annotation = (SequenceType?) Parse(SequenceTypeSymbolAnnotation);
|
|
|
|
if(annotation == null)
|
|
annotation = (SequenceType?) Parse(SequenceTypeWordAnnotation);
|
|
|
|
if (annotation == null)
|
|
return null;
|
|
|
|
switch (annotation.Value)
|
|
{
|
|
case SequenceType.Once:
|
|
case SequenceType.Cycle:
|
|
case SequenceType.Stopping:
|
|
case SequenceType.Shuffle:
|
|
case (SequenceType.Shuffle | SequenceType.Stopping):
|
|
case (SequenceType.Shuffle | SequenceType.Once):
|
|
break;
|
|
|
|
default:
|
|
Error("Sequence type combination not supported: " + annotation.Value);
|
|
return SequenceType.Stopping;
|
|
}
|
|
|
|
return annotation;
|
|
}
|
|
|
|
protected object SequenceTypeSymbolAnnotation()
|
|
{
|
|
if(_sequenceTypeSymbols == null )
|
|
_sequenceTypeSymbols = new CharacterSet("!&~$ ");
|
|
|
|
var sequenceType = (SequenceType)0;
|
|
var sequenceAnnotations = ParseCharactersFromCharSet(_sequenceTypeSymbols);
|
|
if (sequenceAnnotations == null)
|
|
return null;
|
|
|
|
foreach(char symbolChar in sequenceAnnotations) {
|
|
switch(symbolChar) {
|
|
case '!': sequenceType |= SequenceType.Once; break;
|
|
case '&': sequenceType |= SequenceType.Cycle; break;
|
|
case '~': sequenceType |= SequenceType.Shuffle; break;
|
|
case '$': sequenceType |= SequenceType.Stopping; break;
|
|
}
|
|
}
|
|
|
|
if (sequenceType == (SequenceType)0)
|
|
return null;
|
|
|
|
return sequenceType;
|
|
}
|
|
|
|
CharacterSet _sequenceTypeSymbols = new CharacterSet("!&~$");
|
|
|
|
protected object SequenceTypeWordAnnotation()
|
|
{
|
|
var sequenceTypes = Interleave<SequenceType?>(SequenceTypeSingleWord, Exclude(Whitespace));
|
|
if (sequenceTypes == null || sequenceTypes.Count == 0)
|
|
return null;
|
|
|
|
if (ParseString (":") == null)
|
|
return null;
|
|
|
|
var combinedSequenceType = (SequenceType)0;
|
|
foreach(var seqType in sequenceTypes) {
|
|
combinedSequenceType |= seqType.Value;
|
|
}
|
|
|
|
return combinedSequenceType;
|
|
}
|
|
|
|
protected object SequenceTypeSingleWord()
|
|
{
|
|
SequenceType? seqType = null;
|
|
|
|
var word = Parse(IdentifierWithMetadata);
|
|
if (word != null)
|
|
{
|
|
switch (word.name)
|
|
{
|
|
case "once":
|
|
seqType = SequenceType.Once;
|
|
break;
|
|
case "cycle":
|
|
seqType = SequenceType.Cycle;
|
|
break;
|
|
case "shuffle":
|
|
seqType = SequenceType.Shuffle;
|
|
break;
|
|
case "stopping":
|
|
seqType = SequenceType.Stopping;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (seqType == null)
|
|
return null;
|
|
|
|
return seqType;
|
|
}
|
|
|
|
protected List<ContentList> InnerSequenceObjects()
|
|
{
|
|
var multiline = Parse(Newline) != null;
|
|
|
|
List<ContentList> result = null;
|
|
if (multiline) {
|
|
result = Parse(InnerMultilineSequenceObjects);
|
|
} else {
|
|
result = Parse(InnerInlineSequenceObjects);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected List<ContentList> InnerInlineSequenceObjects()
|
|
{
|
|
var interleavedContentAndPipes = Interleave<object> (Optional (MixedTextAndLogic), String ("|"), flatten:false);
|
|
if (interleavedContentAndPipes == null)
|
|
return null;
|
|
|
|
var result = new List<ContentList> ();
|
|
|
|
// The content and pipes won't necessarily be perfectly interleaved in the sense that
|
|
// the content can be missing, but in that case it's intended that there's blank content.
|
|
bool justHadContent = false;
|
|
foreach (object contentOrPipe in interleavedContentAndPipes) {
|
|
|
|
// Pipe/separator
|
|
if (contentOrPipe as string == "|") {
|
|
|
|
// Expected content, saw pipe - need blank content now
|
|
if (!justHadContent) {
|
|
|
|
// Add blank content
|
|
result.Add (new ContentList ());
|
|
}
|
|
|
|
justHadContent = false;
|
|
}
|
|
|
|
// Real content
|
|
else {
|
|
|
|
var content = contentOrPipe as List<Parsed.Object>;
|
|
if (content == null) {
|
|
Error ("Expected content, but got " + contentOrPipe + " (this is an ink compiler bug!)");
|
|
} else {
|
|
result.Add (new ContentList (content));
|
|
}
|
|
|
|
justHadContent = true;
|
|
}
|
|
}
|
|
|
|
// Ended in a pipe? Need to insert final blank content
|
|
if (!justHadContent)
|
|
result.Add (new ContentList ());
|
|
|
|
return result;
|
|
}
|
|
|
|
protected List<ContentList> InnerMultilineSequenceObjects()
|
|
{
|
|
MultilineWhitespace ();
|
|
|
|
var contentLists = OneOrMore (SingleMultilineSequenceElement);
|
|
if (contentLists == null)
|
|
return null;
|
|
|
|
return contentLists.Cast<ContentList> ().ToList();
|
|
}
|
|
|
|
protected ContentList SingleMultilineSequenceElement()
|
|
{
|
|
Whitespace ();
|
|
|
|
// Make sure we're not accidentally parsing a divert
|
|
if (ParseString ("->") != null)
|
|
return null;
|
|
|
|
if (ParseString ("-") == null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
|
|
List<Parsed.Object> content = StatementsAtLevel (StatementLevel.InnerBlock);
|
|
|
|
if (content == null)
|
|
MultilineWhitespace ();
|
|
|
|
// Add newline at the start of each branch
|
|
else {
|
|
content.Insert (0, new Parsed.Text ("\n"));
|
|
}
|
|
|
|
return new ContentList (content);
|
|
}
|
|
}
|
|
}
|
|
|