public string Process(Table table, string pattern, string code) { var output = new StringBuilder(); if (table.NumRows == 0) return ""; if (string.IsNullOrWhiteSpace(pattern)) pattern = "$EACH\r\n$row\r\n"; var scanner = new Scanner(pattern); var tokens = scanner.GetAllTokens(); // Add EACH as default section token if 'tokens' does not start with any section token if (tokens[0].Category != TokenCategory.Each && tokens[0].Category != TokenCategory.EachPlus && tokens[0].Category != TokenCategory.Once) { tokens.Insert(0, new Token(TokenCategory.Each)); } var tokenList = new TokenList(tokens); do { int rowNoStart; int rowNoSentinel; MapSectionTypeToRowNumbers(tokenList, table, out rowNoStart, out rowNoSentinel); ProcessSection(table, rowNoStart, rowNoSentinel, tokenList, code, output); } while (tokenList.Current.Category != TokenCategory.EndOfInput); return output.ToString(); }
private static void MapSectionTypeToRowNumbers( TokenList tokenList, Table table, out int rowNoStart, out int rowNoSentinel) { switch (tokenList.Values[tokenList.Index++].Category) { case TokenCategory.Once: rowNoStart = 0; rowNoSentinel = 1; break; case TokenCategory.Each: rowNoStart = 0; rowNoSentinel = table.NumRows; if (rowNoSentinel == 0) throw new Exception("Input is empty, but EACH needs at least one row for processing!"); break; case TokenCategory.EachPlus: rowNoStart = 1; rowNoSentinel = table.NumRows; if (rowNoSentinel == 1) throw new Exception("Input is empty or has only one row, but EACH needs at least two rows for processing!"); break; default: throw new Exception("Section token ($ONCE, $EACH, $EACH+) expected"); } }
static void Main(string[] args) { try { if (args.Length != 7) throw new Exception("Usage: TableTweaker.Console <inputFile> <fieldDelimitter> (quotedFields | unquotedFields) <filter> <patternFile> <codeFile> <outputPath>"); var input = File.ReadAllText(args[0]); var fieldDelimiter = string.IsNullOrEmpty(args[1]) ? ',' : args[1][0]; var quotedFields = args[2] == "quotedFields"; var filter = string.IsNullOrEmpty(args[3]) ? ".*" : args[2]; var pattern = string.IsNullOrEmpty(args[4]) ? "" : File.ReadAllText(args[3]); var code = string.IsNullOrEmpty(args[5]) ? "" : File.ReadAllText(args[4]); var outputPath = args[6]; var engine = Engine.Instance; engine.FieldDelimiter = fieldDelimiter; engine.QuotedFields = quotedFields; var table = new Table(input, engine.FieldDelimiter, engine.QuotedFields, filter); var stopwatch = new Stopwatch(); stopwatch.Reset(); stopwatch.Start(); var output = engine.Process(table, pattern, code).Replace("\r", ""); stopwatch.Stop(); File.WriteAllText(outputPath, output); var msg = $"{table.NumRows} filtered input rows processed in {stopwatch.ElapsedMilliseconds} ms"; System.Console.WriteLine(msg); } catch (Exception ex) { System.Console.WriteLine("*** ERROR: " + ex); } #if DEBUG System.Console.ReadLine(); #endif }
private void Process() { try { if (!_windowIsInitialized) { return; // called during initialization! } using (new WaitCursor()) { var filter = Filter; if (string.IsNullOrEmpty(filter)) { filter = ".*"; } var input = GetText(TbxInput); var table = new Table(input, _engine.FieldDelimiter, _engine.QuotedFields, filter); var pattern = new TextRange(TbxPattern.Document.ContentStart, TbxPattern.Document.ContentEnd).Text; var code = MyCodeEditor.Text; _stopwatch.Reset(); _stopwatch.Start(); var output = _engine.Process(table, pattern, code).Replace("\r", ""); _stopwatch.Stop(); SetText(TbxOutput, output); TblMessage.Text = $"{TbxInput.Document.Blocks.Count} unfiltered input rows, {table.NumRows} filtered input rows, {TbxOutput.Document.Blocks.Count} output rows ({_stopwatch.ElapsedMilliseconds} ms)"; } } catch (Exception ex) { MessageBox.Show(ex.Message, @"TableTweaker"); } }
private void Process() { try { if (!_windowIsInitialized) return; // called during initialization! using (new WaitCursor()) { var filter = Filter; if (string.IsNullOrEmpty(filter)) { filter = ".*"; } var input = GetText(TbxInput); var table = new Table(input, _engine.FieldDelimiter, _engine.QuotedFields, filter); var pattern = new TextRange(TbxPattern.Document.ContentStart, TbxPattern.Document.ContentEnd).Text; var code = Editor.Text; _stopwatch.Reset(); _stopwatch.Start(); var output = _engine.Process(table, pattern, code).Replace("\r", ""); _stopwatch.Stop(); SetText(TbxOutput, output); TblMessage.Text = $"{TbxInput.Document.Blocks.Count} unfiltered input rows, {table.NumRows} filtered input rows, {TbxOutput.Document.Blocks.Count} output rows ({_stopwatch.ElapsedMilliseconds} ms)"; } } catch (Exception ex) { MessageBox.Show(ex.Message, @"TableTweaker"); } }
private void ProcessSection(Table table, int rowNoStart, int rowNoSentinel, TokenList tokens, string code, StringBuilder output) { var tokensSectionStartIndex = tokens.Index; for (var rowNo = rowNoStart; rowNo < rowNoSentinel; ++rowNo) { var endOfSection = false; tokens.Index = tokensSectionStartIndex; // reset to 1st token in section while (true) { var token = tokens.Values[tokens.Index++]; string value; int colIndex; switch (token.Category) { case TokenCategory.Text: output.Append(token.Value); break; case TokenCategory.Dollar: output.Append("$"); break; case TokenCategory.HeaderIndex: colIndex = int.Parse(token.Value); CheckColIndex(table, colIndex); value = table.RowFields[0][colIndex]; output.Append(value); break; case TokenCategory.InvertedHeaderIndex: colIndex = table.NumFields - 1 - int.Parse(token.Value); CheckColIndex(table, colIndex); value = table.RowFields[0][colIndex]; output.Append(value); break; case TokenCategory.FieldIndex: colIndex = int.Parse(token.Value); CheckColIndex(table, colIndex); value = table.RowFields[rowNo][colIndex]; output.Append(value); break; case TokenCategory.InvertedFieldIndex: colIndex = table.NumFields - 1 - int.Parse(token.Value); CheckColIndex(table, colIndex); value = table.RowFields[rowNo][colIndex]; output.Append(value); break; case TokenCategory.Header: output.Append(table.Header); break; case TokenCategory.Row: output.Append(table.Rows[rowNo]); break; case TokenCategory.RowNum: output.Append(rowNo); break; case TokenCategory.RowNumOne: output.Append(rowNo + 1); break; case TokenCategory.NumFields: output.Append(table.NumFields); break; case TokenCategory.NumRows: output.Append(table.NumRows); break; case TokenCategory.MethodCall: var pos = token.Value.IndexOfAny("([{<".ToCharArray()); var methodName = token.Value.Substring(0, pos); var args = token.Value.Substring(pos + 1, token.Value.Length - pos - 2); // process args var argsOutput = new StringBuilder(); var argsScanner = new Scanner(args); var argsTokens = new TokenList(argsScanner.GetAllTokens()); ProcessSection(table, rowNo, rowNo + 1, argsTokens, "", argsOutput); var encodedArgs = EncodeQuotationMark(argsOutput.ToString()); var methodCall = $"{methodName}({encodedArgs})"; // without terminating ";" to signal scripting engine that it should return the value! var result = ScriptCSharpCode(code + methodCall); output.Append(result); break; case TokenCategory.Once: case TokenCategory.Each: case TokenCategory.EachPlus: case TokenCategory.EndOfInput: tokens.Index--; endOfSection = true; break; default: throw new Exception("No code implemented for token.Category=" + token.Category); } if (endOfSection) break; } } }
public void ProcessQuotationMarkEngineTest() { var engine = Engine.Instance; var table = new Table("A\"B", CommaFieldDelimitter, false, ""); var output = engine.Process(table, "$ToLower(\"$0\")", "public static string ToLower(string s) { return s.ToLower(); }"); Assert.Equal("a\"b", output); }
private static void CheckColIndex(Table table, int colIndex) { if (colIndex < 0) { throw new Exception("Column index $i can not be negative!"); } if (colIndex >= table.NumFields) { throw new Exception( $"Column index ${colIndex} is 0-based and cannot be larger than $numFields ({table.NumFields})!"); } }
public void CopyQuotationMarkEngineTest() { var engine = Engine.Instance; var table = new Table("A\"B", CommaFieldDelimitter, false, ""); var output = engine.Process(table, "$0", MyCode); Assert.Equal("A\"B", output); }
public void AllTokenPatternEngineTest() { var engine = Engine.Instance; var table = new Table(HeaderAndThreeRowsInput, CommaFieldDelimitter, QuotedFields, ""); var output = engine.Process(table, AllTokenPattern, MyCode); Assert.Equal(HeaderAndThreeRowsAllTokenPatternOutput, output); }
public void InvertedCopyPatternEngineTest() { var engine = Engine.Instance; var table = new Table(HeaderAndThreeRowsInput, CommaFieldDelimitter, QuotedFields, ""); var output = engine.Process(table, InvertedCopyPattern, ""); Assert.Equal(HeaderAndThreeRowsInput, output); }
public void EmptyInputEngineTest() { var engine = Engine.Instance; var input = "\r\n"; var table = new Table(input, CommaFieldDelimitter, QuotedFields, ""); var output = engine.Process(table, "", ""); Assert.Equal(input, output); }
public void TableTestWithQuotedFields() { var table = new Table(HeaderAndThreeRowsInputWithQuotedFields, CommaFieldDelimitter, true, ".*"); Assert.NotNull(table); Assert.Equal(3, table.NumFields); Assert.Equal(4, table.NumRows); Assert.Equal("Last Name,First Name,Company", table.Header); Assert.Equal("Last Name,First Name,Company", table.Rows[0]); Assert.Equal("Jobs,Steve,Apple", table.Rows[1]); Assert.Equal("Cook,Tim,Apple", table.Rows[2]); Assert.Equal("Gates,William \"Bill\",Microsoft", table.Rows[3]); Assert.Equal("Last Name", table.HeaderFields[0]); Assert.Equal("First Name", table.HeaderFields[1]); Assert.Equal("Company", table.HeaderFields[2]); Assert.Equal("Last Name", table.RowFields[0][0]); Assert.Equal("First Name", table.RowFields[0][1]); Assert.Equal("Company", table.RowFields[0][2]); Assert.Equal("Jobs", table.RowFields[1][0]); Assert.Equal("Steve", table.RowFields[1][1]); Assert.Equal("Apple", table.RowFields[1][2]); Assert.Equal("Cook", table.RowFields[2][0]); Assert.Equal("Tim", table.RowFields[2][1]); Assert.Equal("Apple", table.RowFields[2][2]); Assert.Equal("Gates", table.RowFields[3][0]); Assert.Equal("William \"Bill\"", table.RowFields[3][1]); Assert.Equal("Microsoft", table.RowFields[3][2]); }