private static SwitchExpression CompileState <OutputT>(TransducerState <Char, OutputT> state, ParameterExpression reader, ParameterExpression output, LabelTarget @return, Int32 depth) { var idx = 0; var cases = new SwitchCase[state.TransitionTable.Count]; foreach (KeyValuePair <Char, TransducerState <Char, OutputT> > statePair in state.TransitionTable) { cases[idx++] = Expression.SwitchCase( CompileState(statePair.Value, reader, output, @return, depth + 1), Expression.Constant((Char?)statePair.Key) ); } return(Expression.Switch( GExpression.MethodCall <ICodeReader>(reader, r => r.Peek(depth), depth), state.IsTerminal ? Expression.Block( GExpression.MethodCall <ICodeReader>(reader, r => r.Advance(0), depth + 1), Expression.Assign(output, Expression.Constant(state.Output)), Expression.Return(@return, Expression.Constant(true)) ) : (Expression)Expression.Return(@return, Expression.Constant(false)), cases )); }
private static SwitchExpression CompileState <TTokenType, OutputT>(TransducerState <Token <TTokenType>, OutputT> state, ParameterExpression reader, ParameterExpression output, LabelTarget @return, Int32 depth) where TTokenType : notnull { var idx = 0; var cases = new SwitchCase[state.TransitionTable.Count]; foreach (KeyValuePair <Token <TTokenType>, TransducerState <Token <TTokenType>, OutputT> > statePair in state.TransitionTable) { cases[idx++] = Expression.SwitchCase( CompileState(statePair.Value, reader, output, @return, depth + 1), Expression.Constant(statePair.Key) ); } return(Expression.Switch( GExpression.MethodCall <ITokenReader <TTokenType> >(reader, r => r.Lookahead(depth), depth), state.IsTerminal ? Expression.Block( GExpression.MethodCall <ITokenReader <TTokenType> >(reader, r => r.Skip(0), depth + 1), Expression.Assign(output, Expression.Constant(state.Output)), Expression.Return(@return, Expression.Constant(true)) ) : (Expression)Expression.Constant(false), cases )); }
/// <summary> /// Attempts to execute a transducer with content from a <see cref="StringCodeReader" />. If the /// state reached is not a terminal state, the reader is rewinded to it's initial position. /// </summary> /// <typeparam name="OutputT"></typeparam> /// <param name="transducer"></param> /// <param name="reader"></param> /// <param name="output"></param> /// <returns>Whether the state reached was a terminal state.</returns> public static Boolean TryExecute <OutputT>(this Transducer <Char, OutputT> transducer, ICodeReader reader, [MaybeNull] out OutputT output) { if (transducer is null) { throw new ArgumentNullException(nameof(transducer)); } if (reader is null) { throw new ArgumentNullException(nameof(reader)); } var start = reader.Position; TransducerState <Char, OutputT> state = transducer.InitialState; while (reader.Position != reader.Length) { if (!state.TransitionTable.TryGetValue(reader.Peek() !.Value, out TransducerState <Char, OutputT>?tmp)) { break; } state = tmp; reader.Advance(1); } if (state.IsTerminal) { output = state.Output; return(true); } reader.Restore(start); // Since the analyzer doesn't seems to obey [MaybeNull], we ignore the warning output = default; return(false); }
/// <summary> /// Attempts to execute the state machine against the <see cref="TokenReader{TTokenType}" /> /// </summary> /// <typeparam name="TTokenType"></typeparam> /// <typeparam name="OutputT"></typeparam> /// <param name="transducer"></param> /// <param name="reader"></param> /// <param name="output"></param> /// <returns></returns> public static Boolean TryExecute <TTokenType, OutputT>(this Transducer <Token <TTokenType>, OutputT> transducer, ITokenReader <TTokenType> reader, [MaybeNull] out OutputT output) where TTokenType : notnull { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } var offset = 0; TransducerState <Token <TTokenType>, OutputT> state = transducer.InitialState; while (reader.Position < reader.Length) { if (!state.TransitionTable.TryGetValue(reader.Lookahead(offset), out TransducerState <Token <TTokenType>, OutputT>?tmp)) { break; } state = tmp; offset++; } if (state.IsTerminal) { reader.Skip(offset + 1); output = state.Output; return(true); } output = default; return(false); }