mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
172 lines
6.1 KiB
C#
172 lines
6.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace Ink
|
|
{
|
|
public partial class InkParser : StringParser
|
|
{
|
|
public InkParser(string str, string filenameForMetadata = null, Ink.ErrorHandler externalErrorHandler = null, IFileHandler fileHandler = null)
|
|
: this(str, filenameForMetadata, externalErrorHandler, null, fileHandler)
|
|
{ }
|
|
|
|
InkParser(string str, string inkFilename = null, Ink.ErrorHandler externalErrorHandler = null, InkParser rootParser = null, IFileHandler fileHandler = null) : base(str) {
|
|
_filename = inkFilename;
|
|
RegisterExpressionOperators ();
|
|
GenerateStatementLevelRules ();
|
|
|
|
// Built in handler for all standard parse errors and warnings
|
|
this.errorHandler = OnStringParserError;
|
|
|
|
// The above parse errors are then formatted as strings and passed
|
|
// to the Ink.ErrorHandler, or it throws an exception
|
|
_externalErrorHandler = externalErrorHandler;
|
|
|
|
_fileHandler = fileHandler ?? new DefaultFileHandler();
|
|
|
|
if (rootParser == null) {
|
|
_rootParser = this;
|
|
|
|
_openFilenames = new HashSet<string> ();
|
|
|
|
if (inkFilename != null) {
|
|
var fullRootInkPath = _fileHandler.ResolveInkFilename (inkFilename);
|
|
_openFilenames.Add (fullRootInkPath);
|
|
}
|
|
|
|
} else {
|
|
_rootParser = rootParser;
|
|
}
|
|
|
|
}
|
|
|
|
// Main entry point
|
|
public Parsed.Story Parse()
|
|
{
|
|
List<Parsed.Object> topLevelContent = StatementsAtLevel (StatementLevel.Top);
|
|
|
|
// Note we used to return null if there were any errors, but this would mean
|
|
// that include files would return completely empty rather than attempting to
|
|
// continue with errors. Returning an empty include files meant that anything
|
|
// that *did* compile successfully would otherwise be ignored, generating way
|
|
// more errors than necessary.
|
|
return new Parsed.Story (topLevelContent, isInclude:_rootParser != this);
|
|
}
|
|
|
|
protected List<T> SeparatedList<T> (SpecificParseRule<T> mainRule, ParseRule separatorRule) where T : class
|
|
{
|
|
T firstElement = Parse (mainRule);
|
|
if (firstElement == null) return null;
|
|
|
|
var allElements = new List<T> ();
|
|
allElements.Add (firstElement);
|
|
|
|
do {
|
|
|
|
int nextElementRuleId = BeginRule ();
|
|
|
|
var sep = separatorRule ();
|
|
if (sep == null) {
|
|
FailRule (nextElementRuleId);
|
|
break;
|
|
}
|
|
|
|
var nextElement = Parse (mainRule);
|
|
if (nextElement == null) {
|
|
FailRule (nextElementRuleId);
|
|
break;
|
|
}
|
|
|
|
SucceedRule (nextElementRuleId);
|
|
|
|
allElements.Add (nextElement);
|
|
|
|
} while (true);
|
|
|
|
return allElements;
|
|
}
|
|
|
|
protected override string PreProcessInputString(string str)
|
|
{
|
|
var inputWithCommentsRemoved = (new CommentEliminator (str)).Process();
|
|
return inputWithCommentsRemoved;
|
|
}
|
|
|
|
protected Runtime.DebugMetadata CreateDebugMetadata(StringParserState.Element stateAtStart, StringParserState.Element stateAtEnd)
|
|
{
|
|
var md = new Runtime.DebugMetadata ();
|
|
md.startLineNumber = stateAtStart.lineIndex + 1;
|
|
md.endLineNumber = stateAtEnd.lineIndex + 1;
|
|
md.startCharacterNumber = stateAtStart.characterInLineIndex + 1;
|
|
md.endCharacterNumber = stateAtEnd.characterInLineIndex + 1;
|
|
md.fileName = _filename;
|
|
return md;
|
|
}
|
|
|
|
protected override void RuleDidSucceed(object result, StringParserState.Element stateAtStart, StringParserState.Element stateAtEnd)
|
|
{
|
|
// Apply DebugMetadata based on the state at the start of the rule
|
|
// (i.e. use line number as it was at the start of the rule)
|
|
var parsedObj = result as Parsed.Object;
|
|
if ( parsedObj) {
|
|
parsedObj.debugMetadata = CreateDebugMetadata(stateAtStart, stateAtEnd);
|
|
return;
|
|
}
|
|
|
|
// A list of objects that doesn't already have metadata?
|
|
var parsedListObjs = result as List<Parsed.Object>;
|
|
if (parsedListObjs != null) {
|
|
foreach (var parsedListObj in parsedListObjs) {
|
|
if (!parsedListObj.hasOwnDebugMetadata) {
|
|
parsedListObj.debugMetadata = CreateDebugMetadata(stateAtStart, stateAtEnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
var id = result as Parsed.Identifier;
|
|
if (id != null) {
|
|
id.debugMetadata = CreateDebugMetadata(stateAtStart, stateAtEnd);
|
|
}
|
|
}
|
|
|
|
protected bool parsingStringExpression
|
|
{
|
|
get {
|
|
return GetFlag ((uint)CustomFlags.ParsingString);
|
|
}
|
|
set {
|
|
SetFlag ((uint)CustomFlags.ParsingString, value);
|
|
}
|
|
}
|
|
|
|
protected enum CustomFlags {
|
|
ParsingString = 0x1
|
|
}
|
|
|
|
void OnStringParserError(string message, int index, int lineIndex, bool isWarning)
|
|
{
|
|
var warningType = isWarning ? "WARNING:" : "ERROR:";
|
|
string fullMessage;
|
|
|
|
if (_filename != null) {
|
|
fullMessage = string.Format(warningType+" '{0}' line {1}: {2}", _filename, (lineIndex+1), message);
|
|
} else {
|
|
fullMessage = string.Format(warningType+" line {0}: {1}", (lineIndex+1), message);
|
|
}
|
|
|
|
if (_externalErrorHandler != null) {
|
|
_externalErrorHandler (fullMessage, isWarning ? ErrorType.Warning : ErrorType.Error);
|
|
} else {
|
|
throw new System.Exception (fullMessage);
|
|
}
|
|
}
|
|
|
|
IFileHandler _fileHandler;
|
|
|
|
Ink.ErrorHandler _externalErrorHandler;
|
|
|
|
string _filename;
|
|
}
|
|
}
|
|
|