mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
207 lines
6.5 KiB
C#
207 lines
6.5 KiB
C#
using System.Collections.Generic;
|
|
using Ink.Parsed;
|
|
|
|
|
|
namespace Ink
|
|
{
|
|
public partial class InkParser
|
|
{
|
|
protected List<Parsed.Object> MultiDivert()
|
|
{
|
|
Whitespace ();
|
|
|
|
List<Parsed.Object> diverts = null;
|
|
|
|
// Try single thread first
|
|
var threadDivert = Parse(StartThread);
|
|
if (threadDivert) {
|
|
diverts = new List<Object> ();
|
|
diverts.Add (threadDivert);
|
|
return diverts;
|
|
}
|
|
|
|
// Normal diverts and tunnels
|
|
var arrowsAndDiverts = Interleave<object> (
|
|
ParseDivertArrowOrTunnelOnwards,
|
|
DivertIdentifierWithArguments);
|
|
|
|
if (arrowsAndDiverts == null)
|
|
return null;
|
|
|
|
diverts = new List<Parsed.Object> ();
|
|
|
|
// Possible patterns:
|
|
// -> -- explicit gather
|
|
// ->-> -- tunnel onwards
|
|
// -> div -- normal divert
|
|
// ->-> div -- tunnel onwards, followed by override divert
|
|
// -> div -> -- normal tunnel
|
|
// -> div ->-> -- tunnel then tunnel continue
|
|
// -> div -> div -- tunnel then divert
|
|
// -> div -> div -> -- tunnel then tunnel
|
|
// -> div -> div ->->
|
|
// -> div -> div ->-> div (etc)
|
|
|
|
// Look at the arrows and diverts
|
|
for (int i = 0; i < arrowsAndDiverts.Count; ++i) {
|
|
bool isArrow = (i % 2) == 0;
|
|
|
|
// Arrow string
|
|
if (isArrow) {
|
|
|
|
// Tunnel onwards
|
|
if ((string)arrowsAndDiverts [i] == "->->") {
|
|
|
|
bool tunnelOnwardsPlacementValid = (i == 0 || i == arrowsAndDiverts.Count - 1 || i == arrowsAndDiverts.Count - 2);
|
|
if (!tunnelOnwardsPlacementValid)
|
|
Error ("Tunnel onwards '->->' must only come at the begining or the start of a divert");
|
|
|
|
var tunnelOnwards = new TunnelOnwards ();
|
|
if (i < arrowsAndDiverts.Count - 1) {
|
|
var tunnelOnwardDivert = arrowsAndDiverts [i+1] as Parsed.Divert;
|
|
tunnelOnwards.divertAfter = tunnelOnwardDivert;
|
|
}
|
|
|
|
diverts.Add (tunnelOnwards);
|
|
|
|
// Not allowed to do anything after a tunnel onwards.
|
|
// If we had anything left it would be caused in the above Error for
|
|
// the positioning of a ->->
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Divert
|
|
else {
|
|
|
|
var divert = arrowsAndDiverts [i] as Divert;
|
|
|
|
// More to come? (further arrows) Must be tunnelling.
|
|
if (i < arrowsAndDiverts.Count - 1) {
|
|
divert.isTunnel = true;
|
|
}
|
|
|
|
diverts.Add (divert);
|
|
}
|
|
}
|
|
|
|
// Single -> (used for default choices)
|
|
if (diverts.Count == 0 && arrowsAndDiverts.Count == 1) {
|
|
var gatherDivert = new Divert ((Parsed.Object)null);
|
|
gatherDivert.isEmpty = true;
|
|
diverts.Add (gatherDivert);
|
|
|
|
if (!_parsingChoice)
|
|
Error ("Empty diverts (->) are only valid on choices");
|
|
}
|
|
|
|
return diverts;
|
|
}
|
|
|
|
protected Divert StartThread()
|
|
{
|
|
Whitespace ();
|
|
|
|
if (ParseThreadArrow() == null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
var divert = Expect(DivertIdentifierWithArguments, "target for new thread", () => new Divert(null)) as Divert;
|
|
divert.isThread = true;
|
|
|
|
return divert;
|
|
}
|
|
|
|
protected Divert DivertIdentifierWithArguments()
|
|
{
|
|
Whitespace ();
|
|
|
|
List<Identifier> targetComponents = Parse (DotSeparatedDivertPathComponents);
|
|
if (targetComponents == null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
var optionalArguments = Parse(ExpressionFunctionCallArguments);
|
|
|
|
Whitespace ();
|
|
|
|
var targetPath = new Path (targetComponents);
|
|
return new Divert (targetPath, optionalArguments);
|
|
}
|
|
|
|
protected Divert SingleDivert()
|
|
{
|
|
var diverts = Parse (MultiDivert);
|
|
if (diverts == null)
|
|
return null;
|
|
|
|
// Ideally we'd report errors if we get the
|
|
// wrong kind of divert, but unfortunately we
|
|
// have to hack around the fact that sequences use
|
|
// a very similar syntax.
|
|
// i.e. if you have a multi-divert at the start
|
|
// of a sequence, it initially tries to parse it
|
|
// as a divert target (part of an expression of
|
|
// a conditional) and gives errors. So instead
|
|
// we just have to blindly reject it as a single
|
|
// divert, and give a slightly less nice error
|
|
// when you DO use a multi divert as a divert taret.
|
|
|
|
if (diverts.Count != 1) {
|
|
return null;
|
|
}
|
|
|
|
var singleDivert = diverts [0];
|
|
if (singleDivert is TunnelOnwards) {
|
|
return null;
|
|
}
|
|
|
|
var divert = diverts [0] as Divert;
|
|
if (divert.isTunnel) {
|
|
return null;
|
|
}
|
|
|
|
return divert;
|
|
}
|
|
|
|
List<Identifier> DotSeparatedDivertPathComponents()
|
|
{
|
|
return Interleave<Identifier> (Spaced (IdentifierWithMetadata), Exclude (String (".")));
|
|
}
|
|
|
|
protected string ParseDivertArrowOrTunnelOnwards()
|
|
{
|
|
int numArrows = 0;
|
|
while (ParseString ("->") != null)
|
|
numArrows++;
|
|
|
|
if (numArrows == 0)
|
|
return null;
|
|
|
|
else if (numArrows == 1)
|
|
return "->";
|
|
|
|
else if (numArrows == 2)
|
|
return "->->";
|
|
|
|
else {
|
|
Error ("Unexpected number of arrows in divert. Should only have '->' or '->->'");
|
|
return "->->";
|
|
}
|
|
}
|
|
|
|
protected string ParseDivertArrow()
|
|
{
|
|
return ParseString ("->");
|
|
}
|
|
|
|
protected string ParseThreadArrow()
|
|
{
|
|
return ParseString ("<-");
|
|
}
|
|
}
|
|
}
|
|
|