/// <summary> /// Get the line in the input for a given index. /// </summary> /// <param name="memo">Memo to use.</param> /// <param name="index">Index in the input.</param> /// <param name="line">The line number (1-based) of the line.</param> /// <param name="offset">The offset in the line of the given index.</param> /// <returns>The line for a given index.</returns> public static string GetLine(Memo <char, TResult> memo, int index, out int line, out int offset) { int[] begins; object prop; if (memo.Properties.TryGetValue("lineBeginnings", out prop) && prop is int[]) { begins = (int[])prop; } else { memo.Positions.Add(0); begins = memo.Positions.OrderBy(n => n).ToArray(); memo.Properties.Add("lineBeginnings", begins); } int inputIndex, inputNext = int.MaxValue; int arrayIndex = Array.BinarySearch <int>(begins, index); if (arrayIndex >= 0) { line = arrayIndex + 1; offset = 0; inputIndex = begins[arrayIndex]; if ((arrayIndex + 1) < begins.Length) { inputNext = begins[arrayIndex + 1]; } } else { int nextLargestArrayIndex = ~arrayIndex; line = nextLargestArrayIndex; offset = index - begins[nextLargestArrayIndex - 1]; inputIndex = begins[nextLargestArrayIndex - 1]; if (nextLargestArrayIndex < begins.Length) { inputNext = begins[nextLargestArrayIndex]; } } return(new string(memo.InputEnumerable.Skip(inputIndex).Take(inputNext - inputIndex).TakeWhile(ch => ch != '\r' && ch != '\n').ToArray())); }
/// <summary> /// Matches a literal string (only used with matchers on a <c>char</c> enumerable). /// </summary> /// <param name="memo">Memo.</param> /// <param name="index">Index.</param> /// <param name="str">String to match.</param> protected MatchItem <TInput, TResult> _ParseLiteralString(Memo <TInput, TResult> memo, ref int index, string str) { int cur_index = index; bool failed = false; try { foreach (var ch in str) { if (memo.InputString != null && cur_index >= memo.InputString.Length) { failed = true; break; } char cur_ch = memo.InputString != null ? memo.InputString[cur_index] : (char)(object)memo.InputEnumerable.ElementAt(cur_index); ++cur_index; if (cur_ch != ch) { failed = true; break; } } } catch { failed = true; } if (!failed) { var result = new MatchItem <TInput, TResult> { StartIndex = index, NextIndex = cur_index, InputEnumerable = memo.InputEnumerable }; index = cur_index; memo.Results.Push(result); return(result); } memo.Results.Push(null); //memo.AddError(index, () => "expected \"" + Regex.Escape(str) + "\""); return(null); }
/// <summary> /// Try to match the input. /// </summary> /// <param name="input">The input to be matched.</param> /// <param name="production">The top-level grammar production (rule) of the generated parser class to use.</param> /// <returns>The result of the match.</returns> public virtual MatchResult <TInput, TResult> GetMatch(IEnumerable <TInput> input, Action <Memo <TInput, TResult>, int, IEnumerable <MatchItem <TInput, TResult> > > production) { var memo = new Memo <TInput, TResult>(input); MatchItem <TInput, TResult> result = null; if (BeforeMatch != null) { BeforeMatch(memo, input, production); } try { if (Terminals == null) { Terminals = new HashSet <string>(); } result = _MemoCall(memo, production.Method.Name, 0, production, null); } catch (MatcherException me) { memo.ClearErrors(); memo.AddError(me.Index, me.Message); } catch (Exception e) { memo.ClearErrors(); memo.AddError(0, e.Message #if DEBUG + "\n" + e.StackTrace #endif ); } memo.ClearMemoTable(); // allow memo tables to be gc'd var match_result = result != null ? new MatchResult <TInput, TResult>(this, memo, true, result.StartIndex, result.NextIndex, result.Results, memo.LastError, memo.LastErrorIndex) : new MatchResult <TInput, TResult>(this, memo, false, -1, -1, null, memo.LastError, memo.LastErrorIndex); if (AfterMatch != null) { AfterMatch(match_result); } return(match_result); }
/// <summary> /// Matches a set of characters in an argument stream. /// </summary> /// <param name="memo">Memo.</param> /// <param name="item_index">Item index.</param> /// <param name="input_index">Input index.</param> /// <param name="args">Argument stream.</param> /// <param name="chars">Characters to match.</param> protected MatchItem <TInput, TResult> _ParseInputClassArgs(Memo <TInput, TResult> memo, ref int item_index, ref int input_index, IEnumerable <MatchItem <TInput, TResult> > args, params char[] chars) { try { int cur_item_index = item_index; int cur_input_index = input_index; var cur_item = args.ElementAt(cur_item_index); TInput cur_input = cur_item.Inputs.ElementAt(cur_input_index); if (Array.IndexOf(chars, cur_input) != -1) { if (cur_input_index + 1 >= cur_item.Inputs.Count()) { ++cur_item_index; cur_input_index = 0; } else { ++cur_input_index; } var result = new MatchItem <TInput, TResult> { StartIndex = item_index, NextIndex = cur_item_index, Inputs = new List <TInput> { cur_input }, }; item_index = cur_item_index; input_index = cur_input_index; memo.ArgResults.Push(result); return(result); } } catch { } memo.ArgResults.Push(null); //memo.AddError(input_index, () => "expected " + args); return(null); }
#pragma warning restore 0219 /// <summary> /// Matches any input in an argument stream. /// </summary> /// <param name="memo">Memo.</param> /// <param name="item_index">Item index.</param> /// <param name="input_index">Input index.</param> /// <param name="args">Argument stream.</param> protected MatchItem <TInput, TResult> _ParseAnyArgs(Memo <TInput, TResult> memo, ref int item_index, ref int input_index, IEnumerable <MatchItem <TInput, TResult> > args) { if (args != null) { try { if (input_index == 0) { var _temp = args.ElementAt(item_index); ++item_index; memo.ArgResults.Push(_temp); return(_temp); } } catch { } } memo.ArgResults.Push(null); memo.AddError(input_index, () => "not enough arguments"); return(null); }
/// <summary> /// Matches a literal object in an argument stream. /// </summary> /// <param name="memo">Memo.</param> /// <param name="item_index">Item index.</param> /// <param name="input_index">Input index.</param> /// <param name="obj">Object to match.</param> /// <param name="args">Argument stream.</param> protected MatchItem <TInput, TResult> _ParseLiteralArgs(Memo <TInput, TResult> memo, ref int item_index, ref int input_index, object obj, IEnumerable <MatchItem <TInput, TResult> > args) { if (args != null) { try { if (obj is IEnumerable <TInput> ) { int old_item_index = item_index; int cur_item_index = item_index; int cur_input_index = input_index; var input_list = new List <TInput>(); foreach (TInput input in ((IEnumerable <TInput>)obj)) { var cur_item = args.ElementAt(cur_item_index); TInput cur_input = cur_item.Inputs.ElementAt(cur_input_index); if (ObjectEquals(input, cur_input)) { input_list.Add(cur_input); if (cur_input_index + 1 >= cur_item.Inputs.Count()) { ++cur_item_index; cur_input_index = 0; } else { ++input_index; } } else { } } // item_index = cur_item_index; input_index = cur_input_index; var result = new MatchItem <TInput, TResult> { StartIndex = old_item_index, NextIndex = item_index, Inputs = input_list, }; memo.ArgResults.Push(result); return(result); } else { int old_item_index = item_index; var cur_item = args.ElementAt(item_index); TInput cur_input = cur_item.Inputs.ElementAt(input_index); if (ObjectEquals(obj, cur_input)) { // increment if (input_index + 1 >= cur_item.Inputs.Count()) { ++item_index; input_index = 0; } else { ++input_index; } // var result = new MatchItem <TInput, TResult> { StartIndex = old_item_index, NextIndex = item_index, Inputs = new List <TInput> { cur_input }, }; memo.ArgResults.Push(result); return(result); } } } catch { } } memo.ArgResults.Push(null); //memo.AddError(input_index, () => "expected " + obj); return(null); }
/// <summary> /// Matches a literal object. /// </summary> /// <param name="memo">Memo.</param> /// <param name="index">Index.</param> /// <param name="obj">Object to match.</param> protected MatchItem <TInput, TResult> _ParseLiteralObj(Memo <TInput, TResult> memo, ref int index, object obj) { if (obj is IEnumerable <TInput> ) { int cur_index = index; bool failed = false; try { foreach (var input in (IEnumerable <TInput>)obj) { TInput cur_input = memo.InputList != null ? memo.InputList[cur_index] : memo.InputEnumerable.ElementAt(cur_index); ++cur_index; if (!ObjectEquals(input, cur_input)) { failed = true; break; } } } catch { failed = true; } if (!failed) { var result = new MatchItem <TInput, TResult> { StartIndex = index, NextIndex = cur_index, InputEnumerable = memo.InputEnumerable, }; memo.Results.Push(result); index = cur_index; return(result); } } else { try { TInput cur_input = memo.InputList != null ? memo.InputList[index] : memo.InputEnumerable.ElementAt(index); if (ObjectEquals(obj, cur_input)) { var result = new MatchItem <TInput, TResult> { StartIndex = index, NextIndex = index + 1, InputEnumerable = memo.InputEnumerable, }; memo.Results.Push(result); ++index; return(result); } } catch { } } memo.Results.Push(null); //memo.AddError(index, () => "expected " + obj); return(null); }
/// <summary> /// Call a grammar production, using memoization and handling left-recursion. /// </summary> /// <param name="memo">The memo for the current match.</param> /// <param name="ruleName">The name of the production.</param> /// <param name="index">The current index in the input stream.</param> /// <param name="production">The production itself.</param> /// <param name="args">Arguments to the production (can be null).</param> /// <returns>The result of the production at the given input index.</returns> protected MatchItem <TInput, TResult> _MemoCall ( Memo <TInput, TResult> memo, string ruleName, int index, Action <Memo <TInput, TResult>, int, IEnumerable <MatchItem <TInput, TResult> > > production, IEnumerable <MatchItem <TInput, TResult> > args ) { MatchItem <TInput, TResult> result; var expansion = new Expansion { Name = args == null ? ruleName : ruleName + string.Join(", ", args.Select(arg => arg.ToString()).ToArray()), Num = 0 }; // if we have a memo record, use that if (memo.TryGetMemo(expansion, index, out result)) { memo.Results.Push(result); return(result); } // if we are not handling left recursion, just call the production directly. if (!HandleLeftRecursion || Terminals.Contains(ruleName)) { production(memo, index, args); result = memo.Results.Peek(); memo.Memoize(expansion, index, result); if (result == null) { memo.AddError(index, () => "expected " + ruleName); } return(result); } // check for left-recursion LRRecord <MatchItem <TInput, TResult> > record; if (memo.TryGetLRRecord(expansion, index, out record)) { record.LRDetected = true; var involved = memo.CallStack .TakeWhile(rec => rec.CurrentExpansion.Name != expansion.Name) .Select(rec => rec.CurrentExpansion.Name); if (record.InvolvedRules != null) { record.InvolvedRules.UnionWith(involved); } else { record.InvolvedRules = new HashSet <string>(involved); } if (!memo.TryGetMemo(record.CurrentExpansion, index, out result)) { throw new MatcherException(index, "Problem with expansion " + record.CurrentExpansion); } memo.Results.Push(result); } // no lr information else { record = new LRRecord <MatchItem <TInput, TResult> >(); record.LRDetected = false; record.NumExpansions = 1; record.CurrentExpansion = new Expansion { Name = expansion.Name, Num = record.NumExpansions }; record.CurrentNextIndex = -1; memo.Memoize(record.CurrentExpansion, index, null); memo.StartLRRecord(expansion, index, record); memo.CallStack.Push(record); while (true) { production(memo, index, args); result = memo.Results.Pop(); // do we need to keep trying the expansions? if (record.LRDetected && result != null && result.NextIndex > record.CurrentNextIndex) { record.NumExpansions = record.NumExpansions + 1; record.CurrentExpansion = new Expansion { Name = expansion.Name, Num = record.NumExpansions }; record.CurrentNextIndex = result != null ? result.NextIndex : 0; memo.Memoize(record.CurrentExpansion, index, result); record.CurrentResult = result; } // we are done trying to expand else { if (record.LRDetected) { result = record.CurrentResult; } memo.ForgetLRRecord(expansion, index); memo.Results.Push(result); // if we are not involved in any left-recursion expansions above us, memoize memo.CallStack.Pop(); bool found_lr = memo.CallStack.Any(rec => rec.InvolvedRules != null && rec.InvolvedRules.Contains(expansion.Name)); if (!found_lr) { memo.Memoize(expansion, index, result); } if (result == null) { memo.AddError(index, () => "expected " + expansion.Name); } break; } } } return(result); }