- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Diagnostics;
- using System.Text;
- namespace Ink
- {
- public class StringParser
- {
- public delegate object ParseRule();
- public delegate T SpecificParseRule<T>() where T : class;
- public delegate void ErrorHandler(string message, int index, int lineIndex, bool isWarning);
- public StringParser (string str)
- {
- str = PreProcessInputString (str);
- state = new StringParserState();
- if (str != null) {
- _chars = str.ToCharArray ();
- } else {
- _chars = new char[0];
- }
- inputString = str;
- }
- public class ParseSuccessStruct {};
- public static ParseSuccessStruct ParseSuccess = new ParseSuccessStruct();
- public static CharacterSet numbersCharacterSet = new CharacterSet("0123456789");
- protected ErrorHandler errorHandler { get; set; }
- public char currentCharacter
- {
- get
- {
- if (index >= 0 && remainingLength > 0) {
- return _chars [index];
- } else {
- return (char)0;
- }
- }
- }
- public StringParserState state { get; private set; }
- public bool hadError { get; protected set; }
-
-
- protected virtual string PreProcessInputString(string str)
- {
- return str;
- }
-
-
-
- protected int BeginRule()
- {
- return state.Push ();
- }
- protected object FailRule(int expectedRuleId)
- {
- state.Pop (expectedRuleId);
- return null;
- }
- protected void CancelRule(int expectedRuleId)
- {
- state.Pop (expectedRuleId);
- }
- protected object SucceedRule(int expectedRuleId, object result = null)
- {
-
- var stateAtSucceedRule = state.Peek(expectedRuleId);
- var stateAtBeginRule = state.PeekPenultimate ();
-
- RuleDidSucceed (result, stateAtBeginRule, stateAtSucceedRule);
-
-
- state.Squash();
- if (result == null) {
- result = ParseSuccess;
- }
- return result;
- }
- protected virtual void RuleDidSucceed(object result, StringParserState.Element startState, StringParserState.Element endState)
- {
- }
- protected object Expect(ParseRule rule, string message = null, ParseRule recoveryRule = null)
- {
- object result = ParseObject(rule);
- if (result == null) {
- if (message == null) {
- message = rule.Method.Name;
- }
- string butSaw;
- string lineRemainder = LineRemainder ();
- if (lineRemainder == null || lineRemainder.Length == 0) {
- butSaw = "end of line";
- } else {
- butSaw = "'" + lineRemainder + "'";
- }
- Error ("Expected "+message+" but saw "+butSaw);
- if (recoveryRule != null) {
- result = recoveryRule ();
- }
- }
- return result;
- }
- protected void Error(string message, bool isWarning = false)
- {
- ErrorOnLine (message, lineIndex + 1, isWarning);
- }
- protected void ErrorWithParsedObject(string message, Parsed.Object result, bool isWarning = false)
- {
- ErrorOnLine (message, result.debugMetadata.startLineNumber, isWarning);
- }
- protected void ErrorOnLine(string message, int lineNumber, bool isWarning)
- {
- if ( !state.errorReportedAlreadyInScope ) {
- var errorType = isWarning ? "Warning" : "Error";
- if (errorHandler == null) {
- throw new System.Exception (errorType+" on line " + lineNumber + ": " + message);
- } else {
- errorHandler (message, index, lineNumber-1, isWarning);
- }
- state.NoteErrorReported ();
- }
- if( !isWarning )
- hadError = true;
- }
- protected void Warning(string message)
- {
- Error(message, isWarning:true);
- }
- public bool endOfInput
- {
- get { return index >= _chars.Length; }
- }
- public string remainingString
- {
- get {
- return new string(_chars, index, remainingLength);
- }
- }
- public string LineRemainder()
- {
- return (string) Peek (() => ParseUntilCharactersFromString ("\n\r"));
- }
- public int remainingLength
- {
- get {
- return _chars.Length - index;
- }
- }
- public string inputString { get; private set; }
- public int lineIndex
- {
- set {
- state.lineIndex = value;
- }
- get {
- return state.lineIndex;
- }
- }
- public int characterInLineIndex
- {
- set {
- state.characterInLineIndex = value;
- }
- get {
- return state.characterInLineIndex;
- }
- }
- public int index
- {
-
-
-
-
- private set {
- state.characterIndex = value;
- }
- get {
- return state.characterIndex;
- }
- }
- public void SetFlag(uint flag, bool trueOrFalse) {
- if (trueOrFalse) {
- state.customFlags |= flag;
- } else {
- state.customFlags &= ~flag;
- }
- }
- public bool GetFlag(uint flag) {
- return (state.customFlags & flag) != 0;
- }
-
-
-
- public object ParseObject(ParseRule rule)
- {
- int ruleId = BeginRule ();
- var stackHeightBefore = state.stackHeight;
- var result = rule ();
- if (stackHeightBefore != state.stackHeight) {
- throw new System.Exception ("Mismatched Begin/Fail/Succeed rules");
- }
- if (result == null)
- return FailRule (ruleId);
- SucceedRule (ruleId, result);
- return result;
- }
- public T Parse<T>(SpecificParseRule<T> rule) where T : class
- {
- int ruleId = BeginRule ();
- var result = rule () as T;
- if (result == null) {
- FailRule (ruleId);
- return null;
- }
- SucceedRule (ruleId, result);
- return result;
- }
- public object OneOf(params ParseRule[] array)
- {
- foreach (ParseRule rule in array) {
- object result = ParseObject(rule);
- if (result != null)
- return result;
- }
- return null;
- }
- public List<object> OneOrMore(ParseRule rule)
- {
- var results = new List<object> ();
- object result = null;
- do {
- result = ParseObject(rule);
- if( result != null ) {
- results.Add(result);
- }
- } while(result != null);
- if (results.Count > 0) {
- return results;
- } else {
- return null;
- }
- }
- public ParseRule Optional(ParseRule rule)
- {
- return () => {
- object result = ParseObject(rule);
- if( result == null ) {
- result = ParseSuccess;
- }
- return result;
- };
- }
-
-
- public ParseRule Exclude(ParseRule rule)
- {
- return () => {
- object result = ParseObject(rule);
- if( result == null ) {
- return null;
- }
- return ParseSuccess;
- };
- }
-
- public ParseRule OptionalExclude(ParseRule rule)
- {
- return () => {
- ParseObject(rule);
- return ParseSuccess;
- };
- }
-
-
-
- protected ParseRule String(string str)
- {
- return () => ParseString (str);
- }
- private void TryAddResultToList<T>(object result, List<T> list, bool flatten = true)
- {
- if (result == ParseSuccess) {
- return;
- }
- if (flatten) {
- var resultCollection = result as System.Collections.ICollection;
- if (resultCollection != null) {
- foreach (object obj in resultCollection) {
- Debug.Assert (obj is T);
- list.Add ((T)obj);
- }
- return;
- }
- }
- Debug.Assert (result is T);
- list.Add ((T)result);
- }
- public List<T> Interleave<T>(ParseRule ruleA, ParseRule ruleB, ParseRule untilTerminator = null, bool flatten = true)
- {
- int ruleId = BeginRule ();
- var results = new List<T> ();
-
- var firstA = ParseObject(ruleA);
- if (firstA == null) {
- return (List<T>) FailRule(ruleId);
- } else {
- TryAddResultToList(firstA, results, flatten);
- }
- object lastMainResult = null, outerResult = null;
- do {
-
- if( untilTerminator != null && Peek(untilTerminator) != null ) {
- break;
- }
-
- lastMainResult = ParseObject(ruleB);
- if( lastMainResult == null ) {
- break;
- } else {
- TryAddResultToList(lastMainResult, results, flatten);
- }
-
- outerResult = null;
- if( lastMainResult != null ) {
- outerResult = ParseObject(ruleA);
- if (outerResult == null) {
- break;
- } else {
- TryAddResultToList(outerResult, results, flatten);
- }
- }
-
- } while((lastMainResult != null || outerResult != null)
- && !(lastMainResult == ParseSuccess && outerResult == ParseSuccess) && remainingLength > 0);
- if (results.Count == 0) {
- return (List<T>) FailRule(ruleId);
- }
- return (List<T>) SucceedRule(ruleId, results);
- }
-
-
-
- public string ParseString(string str)
- {
- if (str.Length > remainingLength) {
- return null;
- }
- int ruleId = BeginRule ();
-
-
-
-
- int i = index;
- int cli = characterInLineIndex;
- int li = lineIndex;
- bool success = true;
- foreach (char c in str) {
- if ( _chars[i] != c) {
- success = false;
- break;
- }
- if (c == '\n') {
- li++;
- cli = -1;
- }
- i++;
- cli++;
- }
- index = i;
- characterInLineIndex = cli;
- lineIndex = li;
- if (success) {
- return (string) SucceedRule(ruleId, str);
- }
- else {
- return (string) FailRule (ruleId);
- }
- }
- public char ParseSingleCharacter()
- {
- if (remainingLength > 0) {
- char c = _chars [index];
- if (c == '\n') {
- lineIndex++;
- characterInLineIndex = -1;
- }
- index++;
- characterInLineIndex++;
- return c;
- } else {
- return (char)0;
- }
- }
- public string ParseUntilCharactersFromString(string str, int maxCount = -1)
- {
- return ParseCharactersFromString(str, false, maxCount);
- }
- public string ParseUntilCharactersFromCharSet(CharacterSet charSet, int maxCount = -1)
- {
- return ParseCharactersFromCharSet(charSet, false, maxCount);
- }
- public string ParseCharactersFromString(string str, int maxCount = -1)
- {
- return ParseCharactersFromString(str, true, maxCount);
- }
- public string ParseCharactersFromString(string str, bool shouldIncludeStrChars, int maxCount = -1)
- {
- return ParseCharactersFromCharSet (new CharacterSet(str), shouldIncludeStrChars);
- }
- public string ParseCharactersFromCharSet(CharacterSet charSet, bool shouldIncludeChars = true, int maxCount = -1)
- {
- if (maxCount == -1) {
- maxCount = int.MaxValue;
- }
- int startIndex = index;
-
-
-
-
- int i = index;
- int cli = characterInLineIndex;
- int li = lineIndex;
- int count = 0;
- while ( i < _chars.Length && charSet.Contains (_chars [i]) == shouldIncludeChars && count < maxCount ) {
- if (_chars [i] == '\n') {
- li++;
- cli = -1;
- }
- i++;
- cli++;
- count++;
- }
- index = i;
- characterInLineIndex = cli;
- lineIndex = li;
- int lastCharIndex = index;
- if (lastCharIndex > startIndex) {
- return new string (_chars, startIndex, index - startIndex);
- } else {
- return null;
- }
- }
- public object Peek(ParseRule rule)
- {
- int ruleId = BeginRule ();
- object result = rule ();
- CancelRule (ruleId);
- return result;
- }
- public string ParseUntil(ParseRule stopRule, CharacterSet pauseCharacters = null, CharacterSet endCharacters = null)
- {
- int ruleId = BeginRule ();
- CharacterSet pauseAndEnd = new CharacterSet ();
- if (pauseCharacters != null) {
- pauseAndEnd.UnionWith (pauseCharacters);
- }
- if (endCharacters != null) {
- pauseAndEnd.UnionWith (endCharacters);
- }
- StringBuilder parsedString = new StringBuilder ();
- object ruleResultAtPause = null;
-
-
-
- do {
-
- string partialParsedString = ParseUntilCharactersFromCharSet(pauseAndEnd);
- if( partialParsedString != null ) {
- parsedString.Append(partialParsedString);
- }
-
- ruleResultAtPause = Peek(stopRule);
-
- if( ruleResultAtPause != null ) {
- break;
- } else {
- if( endOfInput ) {
- break;
- }
-
- char pauseCharacter = currentCharacter;
- if( pauseCharacters != null && pauseCharacters.Contains(pauseCharacter) ) {
- parsedString.Append(pauseCharacter);
- if( pauseCharacter == '\n' ) {
- lineIndex++;
- characterInLineIndex = -1;
- }
- index++;
- characterInLineIndex++;
- continue;
- } else {
- break;
- }
- }
- } while(true);
- if (parsedString.Length > 0) {
- return (string) SucceedRule (ruleId, parsedString.ToString ());
- } else {
- return (string) FailRule (ruleId);
- }
- }
-
- public int? ParseInt()
- {
- int oldIndex = index;
- int oldCharacterInLineIndex = characterInLineIndex;
- bool negative = ParseString ("-") != null;
-
- ParseCharactersFromString (" \t");
- var parsedString = ParseCharactersFromCharSet (numbersCharacterSet);
- if(parsedString == null) {
-
- index = oldIndex;
- characterInLineIndex = oldCharacterInLineIndex;
- return null;
- }
- int parsedInt;
- if (int.TryParse (parsedString, out parsedInt)) {
- return negative ? -parsedInt : parsedInt;
- }
- Error("Failed to read integer value: " + parsedString + ". Perhaps it's out of the range of acceptable numbers ink supports? (" + int.MinValue + " to " + int.MaxValue + ")");
- return null;
- }
-
- public float? ParseFloat()
- {
- int oldIndex = index;
- int oldCharacterInLineIndex = characterInLineIndex;
- int? leadingInt = ParseInt ();
- if (leadingInt != null) {
- if (ParseString (".") != null) {
- var afterDecimalPointStr = ParseCharactersFromCharSet (numbersCharacterSet);
- return float.Parse (leadingInt+"." + afterDecimalPointStr, System.Globalization.CultureInfo.InvariantCulture);
- }
- }
-
- index = oldIndex;
- characterInLineIndex = oldCharacterInLineIndex;
- return null;
- }
-
- protected string ParseNewline()
- {
- int ruleId = BeginRule();
-
-
- ParseString ("\r");
- if( ParseString ("\n") == null ) {
- return (string) FailRule(ruleId);
- } else {
- return (string) SucceedRule(ruleId, "\n");
- }
- }
- private char[] _chars;
- }
- }