mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
240 lines
7.5 KiB
C#
240 lines
7.5 KiB
C#
using System.Collections.Generic;
|
|
using Ink.Parsed;
|
|
using System.Linq;
|
|
|
|
namespace Ink
|
|
{
|
|
public partial class InkParser
|
|
{
|
|
protected class NameWithMetadata {
|
|
public string name;
|
|
public Runtime.DebugMetadata metadata;
|
|
}
|
|
|
|
protected class FlowDecl
|
|
{
|
|
public Identifier name;
|
|
public List<FlowBase.Argument> arguments;
|
|
public bool isFunction;
|
|
}
|
|
|
|
protected Knot KnotDefinition()
|
|
{
|
|
var knotDecl = Parse(KnotDeclaration);
|
|
if (knotDecl == null)
|
|
return null;
|
|
|
|
Expect(EndOfLine, "end of line after knot name definition", recoveryRule: SkipToNextLine);
|
|
|
|
ParseRule innerKnotStatements = () => StatementsAtLevel (StatementLevel.Knot);
|
|
|
|
var content = Expect (innerKnotStatements, "at least one line within the knot", recoveryRule: KnotStitchNoContentRecoveryRule) as List<Parsed.Object>;
|
|
|
|
return new Knot (knotDecl.name, content, knotDecl.arguments, knotDecl.isFunction);
|
|
}
|
|
|
|
protected FlowDecl KnotDeclaration()
|
|
{
|
|
Whitespace ();
|
|
|
|
if (KnotTitleEquals () == null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
|
|
Identifier identifier = Parse(IdentifierWithMetadata);
|
|
Identifier knotName;
|
|
|
|
bool isFunc = identifier?.name == "function";
|
|
if (isFunc) {
|
|
Expect (Whitespace, "whitespace after the 'function' keyword");
|
|
knotName = Parse(IdentifierWithMetadata);
|
|
} else {
|
|
knotName = identifier;
|
|
}
|
|
|
|
if (knotName == null) {
|
|
Error ("Expected the name of the " + (isFunc ? "function" : "knot"));
|
|
knotName = new Identifier { name = "" }; // prevent later null ref
|
|
}
|
|
|
|
Whitespace ();
|
|
|
|
List<FlowBase.Argument> parameterNames = Parse (BracketedKnotDeclArguments);
|
|
|
|
Whitespace ();
|
|
|
|
// Optional equals after name
|
|
Parse(KnotTitleEquals);
|
|
|
|
return new FlowDecl () { name = knotName, arguments = parameterNames, isFunction = isFunc };
|
|
}
|
|
|
|
protected string KnotTitleEquals()
|
|
{
|
|
// 2+ "=" starts a knot
|
|
var multiEquals = ParseCharactersFromString ("=");
|
|
if (multiEquals == null || multiEquals.Length <= 1) {
|
|
return null;
|
|
} else {
|
|
return multiEquals;
|
|
}
|
|
}
|
|
|
|
protected object StitchDefinition()
|
|
{
|
|
var decl = Parse(StitchDeclaration);
|
|
if (decl == null)
|
|
return null;
|
|
|
|
Expect(EndOfLine, "end of line after stitch name", recoveryRule: SkipToNextLine);
|
|
|
|
ParseRule innerStitchStatements = () => StatementsAtLevel (StatementLevel.Stitch);
|
|
|
|
var content = Expect(innerStitchStatements, "at least one line within the stitch", recoveryRule: KnotStitchNoContentRecoveryRule) as List<Parsed.Object>;
|
|
|
|
return new Stitch (decl.name, content, decl.arguments, decl.isFunction );
|
|
}
|
|
|
|
protected FlowDecl StitchDeclaration()
|
|
{
|
|
Whitespace ();
|
|
|
|
// Single "=" to define a stitch
|
|
if (ParseString ("=") == null)
|
|
return null;
|
|
|
|
// If there's more than one "=", that's actually a knot definition (or divert), so this rule should fail
|
|
if (ParseString ("=") != null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
// Stitches aren't allowed to be functions, but we parse it anyway and report the error later
|
|
bool isFunc = ParseString ("function") != null;
|
|
if ( isFunc ) {
|
|
Whitespace ();
|
|
}
|
|
|
|
Identifier stitchName = Parse(IdentifierWithMetadata);
|
|
if (stitchName == null)
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
List<FlowBase.Argument> flowArgs = Parse(BracketedKnotDeclArguments);
|
|
|
|
Whitespace ();
|
|
|
|
return new FlowDecl () { name = stitchName, arguments = flowArgs, isFunction = isFunc };
|
|
}
|
|
|
|
|
|
protected object KnotStitchNoContentRecoveryRule()
|
|
{
|
|
// Jump ahead to the next knot or the end of the file
|
|
ParseUntil (KnotDeclaration, new CharacterSet ("="), null);
|
|
|
|
var recoveredFlowContent = new List<Parsed.Object>();
|
|
recoveredFlowContent.Add( new Parsed.Text("<ERROR IN FLOW>" ) );
|
|
return recoveredFlowContent;
|
|
}
|
|
|
|
protected List<FlowBase.Argument> BracketedKnotDeclArguments()
|
|
{
|
|
if (ParseString ("(") == null)
|
|
return null;
|
|
|
|
var flowArguments = Interleave<FlowBase.Argument>(Spaced(FlowDeclArgument), Exclude (String(",")));
|
|
|
|
Expect (String (")"), "closing ')' for parameter list");
|
|
|
|
// If no parameters, create an empty list so that this method is type safe and
|
|
// doesn't attempt to return the ParseSuccess object
|
|
if (flowArguments == null) {
|
|
flowArguments = new List<FlowBase.Argument> ();
|
|
}
|
|
|
|
return flowArguments;
|
|
}
|
|
|
|
protected FlowBase.Argument FlowDeclArgument()
|
|
{
|
|
// Possible forms:
|
|
// name
|
|
// -> name (variable divert target argument
|
|
// ref name
|
|
// ref -> name (variable divert target by reference)
|
|
var firstIden = Parse(IdentifierWithMetadata);
|
|
Whitespace ();
|
|
var divertArrow = ParseDivertArrow ();
|
|
Whitespace ();
|
|
var secondIden = Parse(IdentifierWithMetadata);
|
|
|
|
if (firstIden == null && secondIden == null)
|
|
return null;
|
|
|
|
|
|
var flowArg = new FlowBase.Argument ();
|
|
if (divertArrow != null) {
|
|
flowArg.isDivertTarget = true;
|
|
}
|
|
|
|
// Passing by reference
|
|
if (firstIden != null && firstIden.name == "ref") {
|
|
|
|
if (secondIden == null) {
|
|
Error ("Expected an parameter name after 'ref'");
|
|
}
|
|
|
|
flowArg.identifier = secondIden;
|
|
flowArg.isByReference = true;
|
|
}
|
|
|
|
// Simple argument name
|
|
else {
|
|
|
|
if (flowArg.isDivertTarget) {
|
|
flowArg.identifier = secondIden;
|
|
} else {
|
|
flowArg.identifier = firstIden;
|
|
}
|
|
|
|
if (flowArg.identifier == null) {
|
|
Error ("Expected an parameter name");
|
|
}
|
|
|
|
flowArg.isByReference = false;
|
|
}
|
|
|
|
return flowArg;
|
|
}
|
|
|
|
protected ExternalDeclaration ExternalDeclaration()
|
|
{
|
|
Whitespace ();
|
|
|
|
Identifier external = Parse(IdentifierWithMetadata);
|
|
if (external == null || external.name != "EXTERNAL")
|
|
return null;
|
|
|
|
Whitespace ();
|
|
|
|
var funcIdentifier = Expect(IdentifierWithMetadata, "name of external function") as Identifier ?? new Identifier();
|
|
|
|
Whitespace ();
|
|
|
|
var parameterNames = Expect (BracketedKnotDeclArguments, "declaration of arguments for EXTERNAL, even if empty, i.e. 'EXTERNAL "+funcIdentifier+"()'") as List<FlowBase.Argument>;
|
|
if (parameterNames == null)
|
|
parameterNames = new List<FlowBase.Argument> ();
|
|
|
|
var argNames = parameterNames.Select (arg => arg.identifier?.name).ToList();
|
|
|
|
return new ExternalDeclaration (funcIdentifier, argNames);
|
|
}
|
|
|
|
}
|
|
}
|
|
|