/// <summary> /// Gets the value of the token. /// </summary> /// <param name="token"></param> /// <returns></returns> protected virtual string GetTokenValue(Token token) { ParseEventHandler handler = this.Events[EventParseToken] as ParseEventHandler; if (handler != null) { return(handler(this, new ParseEventArgs(token))); } return(token.Value); }
/// <summary> /// Parses a string representation of an s-expression /// /// Expression ::= Atom | SExpr /// Atom ::= ValidName | Number | QuotedString /// SExpr ::= "(" Atom | SExpr ")" /// /// Number ::= ^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]*\\.?[0-9]+)?$ /// ValidName ::= ^[a-zA-Z_]([a-zA-Z0-9_])*$ /// QuotedString ::= single or doubly quoted text of arbitrary length, with or without whitespace /// /// </summary> /// <param name="psz">Input string to be parsed</param> /// <param name="parseEventHandler">Function to be notified of Parse events</param> /// <param name="context">Any object to be passed along to the notification sink</param> /// <returns>The context object if successful, null if not</returns> /// /// <example> /// /// Given the following support objects /// ///internal class EvaluatorContext ///{ /// public Expression Root { get; private set; } /// public Expression Current { get; set; } /// /// public EvaluatorContext(Expression Root) /// { /// this.Root = Root; /// this.Current = Root; /// } ///} /// ///protected static void ParseEventHandler(ParseEventArgs e) ///{ /// EvaluatorContext pctx = e.ParserContext as EvaluatorContext; /// if (pctx == null) return; /// /// switch (e.Token) /// { /// case E_TOKEN.Comment: /// return; /// /// case E_TOKEN.SExprStart: /// { /// /// a new list child /// pctx.Current = new Expression(pctx.Current, "CHILD >", e.State, e.Token); /// } /// return; /// /// case E_TOKEN.SExprFinish: /// { /// /// pop back one level /// pctx.Current = pctx.Current.Parent; /// } /// return; /// } /// /// switch (e.State) /// { /// case E_PARSESTATE.Atom: /// { /// /// a new atom child /// Expression Atom = new Expression(pctx.Current, e.TokenValue, e.State, e.Token); /// } /// return; /// /// case E_PARSESTATE.Failure: /// { /// throw new ParseException(String.Format("*** FAILURE {0}] : [ {1} ], {2}, {3}", e.ErrorDescription, e.TokenValue, e.State, e.Token)); /// } /// } ///} /// ///we can write ... /// ///EvaluatorContext Context = "(+ 'Hello' ' ' 'World ')".Parse(ParseEventHandler, new EvaluatorContext(new Expression())); ///Expression exHelloWorld = Context.Root.Evaluate(null); /// ///System.Console.WriteLine /// /// </example> /// public static object Parse(this string psz, ParseEventHandler parseEventHandler, object context) { var sStart = 0; var sFinish = 0; var exprNest = 0; var state = ParseState.Start; while (sFinish <= psz.Length) { switch (state) { case ParseState.Start: { exprNest = 0; if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, state, Token.MAX, 0, 0)); } state = ParseState.AtomOrSExpr; } break; case ParseState.Finish: { if (exprNest != 0) { state = ParseState.Failure; break; } if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, state, Token.MAX, 0, 0)); } // we're done... return(context); } } var tok = Tokenizer.NextToken(psz, sFinish, out sStart, out sFinish); // handle comments and get them out of the way switch (tok) { case Token.CommentStart: // /* { Tokenizer.SnarfComment(psz, sStart, out sStart, out sFinish); if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.Comment, Token.CommentStart, sStart, sFinish)); } } continue; } switch (state) { case ParseState.SExprStart: { // SExpr ::= SExprStart (Atom | SExpr)* SExprFinish switch (tok) { case Token.SExprStart: // ( { exprNest++; if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.SExprStart, tok, sStart, sFinish)); } state = ParseState.AtomOrSExpr; } break; case Token.EOF: { sFinish = sStart; state = ParseState.Finish; } break; default: { if (parseEventHandler != null) { parseEventHandler( new ParseEventArgs( psz, context, ParseState.SExprStart, tok, sStart, sFinish, "Unexpected token found instead of SExprStart")); } state = ParseState.Failure; } break; } } break; case ParseState.AtomOrSExpr: { switch (tok) { case Token.DoubleQuotedText: case Token.SingleQuotedText: { if (parseEventHandler != null) { var strToken = psz.Substring(sStart, sFinish - sStart); var fContainsWhitespace = Regex.IsMatch(strToken, ".*[\\s]+.*"); var cSkip = (fContainsWhitespace ? 0 : 1); parseEventHandler(new ParseEventArgs(psz, context, ParseState.Atom, tok, sStart + cSkip, sFinish - cSkip)); } state = ParseState.AtomOrSExpr; } break; case Token.ValidName: case Token.ValidNumber: case Token.Text: { if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.Atom, tok, sStart, sFinish)); } state = ParseState.AtomOrSExpr; } break; case Token.SExprStart: // ( { sFinish = sStart; //rewind token state = ParseState.SExprStart; } break; case Token.SExprFinish: // ) { sFinish = sStart; //rewind token state = ParseState.SExprFinish; } break; case Token.EOF: { state = ParseState.Finish; } break; default: { if (parseEventHandler != null) { parseEventHandler( new ParseEventArgs( psz, context, ParseState.Failure, tok, sStart, sFinish, "Unexpected token found without matching SExprFinish")); } state = ParseState.Failure; } break; } } break; case ParseState.SExprFinish: { switch (tok) { case Token.SExprFinish: // ) { exprNest--; if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.SExprFinish, tok, sStart, sFinish)); } state = (exprNest == 0) ? ParseState.Finish : ParseState.AtomOrSExpr; } break; case Token.EOF: { sFinish = sStart; state = ParseState.Finish; } break; default: { if (parseEventHandler != null) { parseEventHandler( new ParseEventArgs( psz, context, ParseState.Failure, tok, sStart, sFinish, "Unexpected token found instead of SExprFinish")); } state = ParseState.Failure; } break; } } break; // ReSharper disable RedundantCaseLabel case ParseState.Failure: // ReSharper restore RedundantCaseLabel default: { if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.Failure, Token.MAX, sStart, sFinish, "Parser Failure")); } } return(null); } } // while (curpos < psz.Length) if (parseEventHandler != null) { parseEventHandler(new ParseEventArgs(psz, context, ParseState.Failure, Token.MAX, sStart, sFinish)); } return(null); }