Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / InkParser / InkParser_Knot.cs
  1. using System.Collections.Generic;
  2. using Ink.Parsed;
  3. using System.Linq;
  4. namespace Ink
  5. {
  6. public partial class InkParser
  7. {
  8. protected class NameWithMetadata {
  9. public string name;
  10. public Runtime.DebugMetadata metadata;
  11. }
  12. protected class FlowDecl
  13. {
  14. public Identifier name;
  15. public List<FlowBase.Argument> arguments;
  16. public bool isFunction;
  17. }
  18. protected Knot KnotDefinition()
  19. {
  20. var knotDecl = Parse(KnotDeclaration);
  21. if (knotDecl == null)
  22. return null;
  23. Expect(EndOfLine, "end of line after knot name definition", recoveryRule: SkipToNextLine);
  24. ParseRule innerKnotStatements = () => StatementsAtLevel (StatementLevel.Knot);
  25. var content = Expect (innerKnotStatements, "at least one line within the knot", recoveryRule: KnotStitchNoContentRecoveryRule) as List<Parsed.Object>;
  26. return new Knot (knotDecl.name, content, knotDecl.arguments, knotDecl.isFunction);
  27. }
  28. protected FlowDecl KnotDeclaration()
  29. {
  30. Whitespace ();
  31. if (KnotTitleEquals () == null)
  32. return null;
  33. Whitespace ();
  34. Identifier identifier = Parse(IdentifierWithMetadata);
  35. Identifier knotName;
  36. bool isFunc = identifier?.name == "function";
  37. if (isFunc) {
  38. Expect (Whitespace, "whitespace after the 'function' keyword");
  39. knotName = Parse(IdentifierWithMetadata);
  40. } else {
  41. knotName = identifier;
  42. }
  43. if (knotName == null) {
  44. Error ("Expected the name of the " + (isFunc ? "function" : "knot"));
  45. knotName = new Identifier { name = "" }; // prevent later null ref
  46. }
  47. Whitespace ();
  48. List<FlowBase.Argument> parameterNames = Parse (BracketedKnotDeclArguments);
  49. Whitespace ();
  50. // Optional equals after name
  51. Parse(KnotTitleEquals);
  52. return new FlowDecl () { name = knotName, arguments = parameterNames, isFunction = isFunc };
  53. }
  54. protected string KnotTitleEquals()
  55. {
  56. // 2+ "=" starts a knot
  57. var multiEquals = ParseCharactersFromString ("=");
  58. if (multiEquals == null || multiEquals.Length <= 1) {
  59. return null;
  60. } else {
  61. return multiEquals;
  62. }
  63. }
  64. protected object StitchDefinition()
  65. {
  66. var decl = Parse(StitchDeclaration);
  67. if (decl == null)
  68. return null;
  69. Expect(EndOfLine, "end of line after stitch name", recoveryRule: SkipToNextLine);
  70. ParseRule innerStitchStatements = () => StatementsAtLevel (StatementLevel.Stitch);
  71. var content = Expect(innerStitchStatements, "at least one line within the stitch", recoveryRule: KnotStitchNoContentRecoveryRule) as List<Parsed.Object>;
  72. return new Stitch (decl.name, content, decl.arguments, decl.isFunction );
  73. }
  74. protected FlowDecl StitchDeclaration()
  75. {
  76. Whitespace ();
  77. // Single "=" to define a stitch
  78. if (ParseString ("=") == null)
  79. return null;
  80. // If there's more than one "=", that's actually a knot definition (or divert), so this rule should fail
  81. if (ParseString ("=") != null)
  82. return null;
  83. Whitespace ();
  84. // Stitches aren't allowed to be functions, but we parse it anyway and report the error later
  85. bool isFunc = ParseString ("function") != null;
  86. if ( isFunc ) {
  87. Whitespace ();
  88. }
  89. Identifier stitchName = Parse(IdentifierWithMetadata);
  90. if (stitchName == null)
  91. return null;
  92. Whitespace ();
  93. List<FlowBase.Argument> flowArgs = Parse(BracketedKnotDeclArguments);
  94. Whitespace ();
  95. return new FlowDecl () { name = stitchName, arguments = flowArgs, isFunction = isFunc };
  96. }
  97. protected object KnotStitchNoContentRecoveryRule()
  98. {
  99. // Jump ahead to the next knot or the end of the file
  100. ParseUntil (KnotDeclaration, new CharacterSet ("="), null);
  101. var recoveredFlowContent = new List<Parsed.Object>();
  102. recoveredFlowContent.Add( new Parsed.Text("<ERROR IN FLOW>" ) );
  103. return recoveredFlowContent;
  104. }
  105. protected List<FlowBase.Argument> BracketedKnotDeclArguments()
  106. {
  107. if (ParseString ("(") == null)
  108. return null;
  109. var flowArguments = Interleave<FlowBase.Argument>(Spaced(FlowDeclArgument), Exclude (String(",")));
  110. Expect (String (")"), "closing ')' for parameter list");
  111. // If no parameters, create an empty list so that this method is type safe and
  112. // doesn't attempt to return the ParseSuccess object
  113. if (flowArguments == null) {
  114. flowArguments = new List<FlowBase.Argument> ();
  115. }
  116. return flowArguments;
  117. }
  118. protected FlowBase.Argument FlowDeclArgument()
  119. {
  120. // Possible forms:
  121. // name
  122. // -> name (variable divert target argument
  123. // ref name
  124. // ref -> name (variable divert target by reference)
  125. var firstIden = Parse(IdentifierWithMetadata);
  126. Whitespace ();
  127. var divertArrow = ParseDivertArrow ();
  128. Whitespace ();
  129. var secondIden = Parse(IdentifierWithMetadata);
  130. if (firstIden == null && secondIden == null)
  131. return null;
  132. var flowArg = new FlowBase.Argument ();
  133. if (divertArrow != null) {
  134. flowArg.isDivertTarget = true;
  135. }
  136. // Passing by reference
  137. if (firstIden != null && firstIden.name == "ref") {
  138. if (secondIden == null) {
  139. Error ("Expected an parameter name after 'ref'");
  140. }
  141. flowArg.identifier = secondIden;
  142. flowArg.isByReference = true;
  143. }
  144. // Simple argument name
  145. else {
  146. if (flowArg.isDivertTarget) {
  147. flowArg.identifier = secondIden;
  148. } else {
  149. flowArg.identifier = firstIden;
  150. }
  151. if (flowArg.identifier == null) {
  152. Error ("Expected an parameter name");
  153. }
  154. flowArg.isByReference = false;
  155. }
  156. return flowArg;
  157. }
  158. protected ExternalDeclaration ExternalDeclaration()
  159. {
  160. Whitespace ();
  161. Identifier external = Parse(IdentifierWithMetadata);
  162. if (external == null || external.name != "EXTERNAL")
  163. return null;
  164. Whitespace ();
  165. var funcIdentifier = Expect(IdentifierWithMetadata, "name of external function") as Identifier ?? new Identifier();
  166. Whitespace ();
  167. var parameterNames = Expect (BracketedKnotDeclArguments, "declaration of arguments for EXTERNAL, even if empty, i.e. 'EXTERNAL "+funcIdentifier+"()'") as List<FlowBase.Argument>;
  168. if (parameterNames == null)
  169. parameterNames = new List<FlowBase.Argument> ();
  170. var argNames = parameterNames.Select (arg => arg.identifier?.name).ToList();
  171. return new ExternalDeclaration (funcIdentifier, argNames);
  172. }
  173. }
  174. }