Files
Mementos/Unity/Alternate Genre Jam/Assets/Ink/InkLibs/InkCompiler/InkParser/InkParser_Content.cs
2021-06-30 21:39:19 +10:00

218 lines
7.4 KiB
C#

using Ink.Parsed;
using System.Text;
using System.Collections.Generic;
using System.Linq;
namespace Ink
{
public partial class InkParser
{
void TrimEndWhitespace(List<Parsed.Object> mixedTextAndLogicResults, bool terminateWithSpace)
{
// Trim whitespace from end
if (mixedTextAndLogicResults.Count > 0) {
var lastObjIdx = mixedTextAndLogicResults.Count - 1;
var lastObj = mixedTextAndLogicResults[lastObjIdx];
if (lastObj is Text) {
var text = (Text)lastObj;
text.text = text.text.TrimEnd (' ', '\t');
if (terminateWithSpace)
text.text += " ";
// No content left at all? trim the whole object
else if( text.text.Length == 0 ) {
mixedTextAndLogicResults.RemoveAt(lastObjIdx);
// Recurse in case there's more whitespace
TrimEndWhitespace(mixedTextAndLogicResults, terminateWithSpace:false);
}
}
}
}
protected List<Parsed.Object> LineOfMixedTextAndLogic()
{
// Consume any whitespace at the start of the line
// (Except for escaped whitespace)
Parse (Whitespace);
var result = Parse(MixedTextAndLogic);
// Terminating tag
bool onlyTags = false;
var tags = Parse (Tags);
if (tags != null) {
if (result == null) {
result = tags.Cast<Parsed.Object> ().ToList ();
onlyTags = true;
} else {
foreach (var tag in tags) {
result.Add(tag);
}
}
}
if (result == null || result.Count == 0)
return null;
// Warn about accidentally writing "return" without "~"
var firstText = result[0] as Text;
if (firstText) {
if (firstText.text.StartsWith ("return")) {
Warning ("Do you need a '~' before 'return'? If not, perhaps use a glue: <> (since it's lowercase) or rewrite somehow?");
}
}
if (result.Count == 0)
return null;
var lastObj = result [result.Count - 1];
if (!(lastObj is Divert)) {
TrimEndWhitespace (result, terminateWithSpace:false);
}
// Add newline since it's the end of the line
// (so long as it's a line with only tags)
if( !onlyTags )
result.Add (new Text ("\n"));
Expect(EndOfLine, "end of line", recoveryRule: SkipToNextLine);
return result;
}
protected List<Parsed.Object> MixedTextAndLogic()
{
// Check for disallowed "~" within this context
var disallowedTilda = ParseObject(Spaced(String("~")));
if (disallowedTilda != null)
Error ("You shouldn't use a '~' here - tildas are for logic that's on its own line. To do inline logic, use { curly braces } instead");
// Either, or both interleaved
var results = Interleave<Parsed.Object>(Optional (ContentText), Optional (InlineLogicOrGlue));
// Terminating divert?
// (When parsing content for the text of a choice, diverts aren't allowed.
// The divert on the end of the body of a choice is handled specially.)
if (!_parsingChoice) {
var diverts = Parse (MultiDivert);
if (diverts != null) {
// May not have had any results at all if there's *only* a divert!
if (results == null)
results = new List<Parsed.Object> ();
TrimEndWhitespace (results, terminateWithSpace:true);
results.AddRange (diverts);
}
}
if (results == null)
return null;
return results;
}
protected Parsed.Text ContentText()
{
return ContentTextAllowingEcapeChar ();
}
protected Parsed.Text ContentTextAllowingEcapeChar()
{
StringBuilder sb = null;
do {
var str = Parse(ContentTextNoEscape);
bool gotEscapeChar = ParseString(@"\") != null;
if( gotEscapeChar || str != null ) {
if( sb == null ) {
sb = new StringBuilder();
}
if( str != null ) {
sb.Append(str);
}
if( gotEscapeChar ) {
char c = ParseSingleCharacter();
sb.Append(c);
}
} else {
break;
}
} while(true);
if (sb != null ) {
return new Parsed.Text (sb.ToString ());
} else {
return null;
}
}
// Content text is an unusual parse rule compared with most since it's
// less about saying "this is is the small selection of stuff that we parse"
// and more "we parse ANYTHING except this small selection of stuff".
protected string ContentTextNoEscape()
{
// Eat through text, pausing at the following characters, and
// attempt to parse the nonTextRule.
// "-": possible start of divert or start of gather
// "<": possible start of glue
if (_nonTextPauseCharacters == null) {
_nonTextPauseCharacters = new CharacterSet ("-<");
}
// If we hit any of these characters, we stop *immediately* without bothering to even check the nonTextRule
// "{" for start of logic
// "|" for mid logic branch
if (_nonTextEndCharacters == null) {
_nonTextEndCharacters = new CharacterSet ("{}|\n\r\\#");
_notTextEndCharactersChoice = new CharacterSet (_nonTextEndCharacters);
_notTextEndCharactersChoice.AddCharacters ("[]");
_notTextEndCharactersString = new CharacterSet (_nonTextEndCharacters);
_notTextEndCharactersString.AddCharacters ("\"");
}
// When the ParseUntil pauses, check these rules in case they evaluate successfully
ParseRule nonTextRule = () => OneOf (ParseDivertArrow, ParseThreadArrow, EndOfLine, Glue);
CharacterSet endChars = null;
if (parsingStringExpression) {
endChars = _notTextEndCharactersString;
}
else if (_parsingChoice) {
endChars = _notTextEndCharactersChoice;
}
else {
endChars = _nonTextEndCharacters;
}
string pureTextContent = ParseUntil (nonTextRule, _nonTextPauseCharacters, endChars);
if (pureTextContent != null ) {
return pureTextContent;
} else {
return null;
}
}
CharacterSet _nonTextPauseCharacters;
CharacterSet _nonTextEndCharacters;
CharacterSet _notTextEndCharactersChoice;
CharacterSet _notTextEndCharactersString;
}
}