diff options
author | Timothee Cour <timothee.cour2@gmail.com> | 2019-01-09 00:46:44 -0800 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2019-01-09 09:46:44 +0100 |
commit | 23c1ee982e2d3795001879a4527581f33875cd33 (patch) | |
tree | 799acb5b12ca6f24796213a54d4ed9baf6d70beb | |
parent | 258952832f312a25d0ab9771237a547297b336b8 (diff) | |
download | Nim-23c1ee982e2d3795001879a4527581f33875cd33.tar.gz |
add `alignTable`, `parseTableCells` to align/format a tab(etc) delimited table (#10182)
* add compiler/unittest_light.nim for easy diffing: assertEquals and mismatch * fixup * add alignTable, parseTableCells
-rw-r--r-- | compiler/asciitables.nim | 83 | ||||
-rw-r--r-- | compiler/unittest_light.nim | 37 | ||||
-rw-r--r-- | tests/compiler/nim.cfg | 7 | ||||
-rw-r--r-- | tests/compiler/tasciitables.nim | 109 | ||||
-rw-r--r-- | tests/compiler/tunittest_light.nim | 55 |
5 files changed, 291 insertions, 0 deletions
diff --git a/compiler/asciitables.nim b/compiler/asciitables.nim new file mode 100644 index 000000000..c25d54bde --- /dev/null +++ b/compiler/asciitables.nim @@ -0,0 +1,83 @@ +#[ +move to std/asciitables.nim once stable, or to a nimble paackage +once compiler can depend on nimble +]# + +type Cell* = object + text*: string + width*, row*, col*, ncols*, nrows*: int + +iterator parseTableCells*(s: string, delim = '\t'): Cell = + ## iterates over all cells in a `delim`-delimited `s`, after a 1st + ## pass that computes number of rows, columns, and width of each column. + var widths: seq[int] + var cell: Cell + template update() = + if widths.len<=cell.col: + widths.setLen cell.col+1 + widths[cell.col] = cell.width + else: + widths[cell.col] = max(widths[cell.col], cell.width) + cell.width = 0 + + for a in s: + case a + of '\n': + update() + cell.col = 0 + cell.row.inc + elif a == delim: + update() + cell.col.inc + else: + # todo: consider multi-width chars when porting to non-ascii implementation + cell.width.inc + if s.len > 0 and s[^1] != '\n': + update() + + cell.ncols = widths.len + cell.nrows = cell.row + 1 + cell.row = 0 + cell.col = 0 + cell.width = 0 + + template update2() = + cell.width = widths[cell.col] + yield cell + cell.text = "" + cell.width = 0 + cell.col.inc + + template finishRow() = + for col in cell.col..<cell.ncols: + cell.col = col + update2() + cell.col = 0 + + for a in s: + case a + of '\n': + finishRow() + cell.row.inc + elif a == delim: + update2() + else: + cell.width.inc + cell.text.add a + + if s.len > 0 and s[^1] != '\n': + finishRow() + +proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string = + ## formats a `delim`-delimited `s` representing a table; each cell is aligned + ## to a width that's computed for each column; consecutive columns are + ## delimted by `sep`, and alignment space is filled using `fill`. + ## More customized formatting can be done by calling `parseTableCells` directly. + for cell in parseTableCells(s, delim): + result.add cell.text + for i in cell.text.len..<cell.width: + result.add fill + if cell.col < cell.ncols-1: + result.add sep + if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1: + result.add '\n' diff --git a/compiler/unittest_light.nim b/compiler/unittest_light.nim new file mode 100644 index 000000000..bcba6f7c7 --- /dev/null +++ b/compiler/unittest_light.nim @@ -0,0 +1,37 @@ +# note: consider merging tests/assert/testhelper.nim here. + +proc mismatch*[T](lhs: T, rhs: T): string = + ## Simplified version of `unittest.require` that satisfies a common use case, + ## while avoiding pulling too many dependencies. On failure, diagnostic + ## information is provided that in particular makes it easy to spot + ## whitespace mismatches and where the mismatch is. + proc replaceInvisible(s: string): string = + for a in s: + case a + of '\n': result.add "\\n\n" + else: result.add a + + proc quoted(s: string): string = result.addQuoted s + + result.add "\n" + result.add "lhs:{\n" & replaceInvisible( + $lhs) & "}\nrhs:{\n" & replaceInvisible($rhs) & "}\n" + when compiles(lhs.len): + if lhs.len != rhs.len: + result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n" + when compiles(lhs[0]): + var i = 0 + while i < lhs.len and i < rhs.len: + if lhs[i] != rhs[i]: break + i.inc + result.add "first mismatch index: " & $i & "\n" + if i < lhs.len and i < rhs.len: + result.add "lhs[i]: {" & quoted($lhs[i]) & "} rhs[i]: {" & quoted( + $rhs[i]) & "}" + result.add "lhs[0..<i]:{\n" & replaceInvisible($lhs[ + 0..<i]) & "}\nrhs[0..<i]:{\n" & replaceInvisible($rhs[0..<i]) & "}" + +proc assertEquals*[T](lhs: T, rhs: T) = + when false: # can be useful for debugging to see all that's fed to this. + echo "----" & $lhs + doAssert lhs==rhs, mismatch(lhs, rhs) diff --git a/tests/compiler/nim.cfg b/tests/compiler/nim.cfg new file mode 100644 index 000000000..6f49473aa --- /dev/null +++ b/tests/compiler/nim.cfg @@ -0,0 +1,7 @@ +# note: consider moving tests/compilerapi/ to tests/compiler/ since +# that's more predictable. + +# note: without this, tests may succeed locally but fail on CI (it can succeed +# locally even when compiling via `./bin/nim` because `$HOME/.nimble` is being +# used). +--path:"../../" # so we can `import compiler/foo` in this dir diff --git a/tests/compiler/tasciitables.nim b/tests/compiler/tasciitables.nim new file mode 100644 index 000000000..0a5ee0f05 --- /dev/null +++ b/tests/compiler/tasciitables.nim @@ -0,0 +1,109 @@ +import compiler/unittest_light +import compiler/asciitables + +import strformat + +proc alignTableCustom(s: string, delim = '\t', sep = ","): string = + for cell in parseTableCells(s, delim): + result.add fmt"({cell.row},{cell.col}): " + for i in cell.text.len..<cell.width: + result.add " " + result.add cell.text + if cell.col < cell.ncols-1: + result.add sep + if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1: + result.add '\n' + +proc testAlignTable() = + block: # test with variable width columns + var ret = "" + ret.add "12\t143\tbcdef\n" + ret.add "2\t14394852020\tbcdef\n" + ret.add "45342\t1\tbf\n" + ret.add "45342\t1\tbsadfasdfasfdasdff\n" + ret.add "453232323232342\t1\tbsadfasdfasfdasdff\n" + ret.add "45342\t1\tbf\n" + ret.add "45342\t1\tb afasf a ff\n" + ret.add "4\t1\tbf\n" + + assertEquals alignTable(ret), + """ +12 143 bcdef +2 14394852020 bcdef +45342 1 bf +45342 1 bsadfasdfasfdasdff +453232323232342 1 bsadfasdfasfdasdff +45342 1 bf +45342 1 b afasf a ff +4 1 bf +""" + + assertEquals alignTable(ret, fill = '.', sep = ","), + """ +12.............,143........,bcdef............. +2..............,14394852020,bcdef............. +45342..........,1..........,bf................ +45342..........,1..........,bsadfasdfasfdasdff +453232323232342,1..........,bsadfasdfasfdasdff +45342..........,1..........,bf................ +45342..........,1..........,b afasf a ff...... +4..............,1..........,bf................ +""" + + assertEquals alignTableCustom(ret, sep = " "), + """ +(0,0): 12 (0,1): 143 (0,2): bcdef +(1,0): 2 (1,1): 14394852020 (1,2): bcdef +(2,0): 45342 (2,1): 1 (2,2): bf +(3,0): 45342 (3,1): 1 (3,2): bsadfasdfasfdasdff +(4,0): 453232323232342 (4,1): 1 (4,2): bsadfasdfasfdasdff +(5,0): 45342 (5,1): 1 (5,2): bf +(6,0): 45342 (6,1): 1 (6,2): b afasf a ff +(7,0): 4 (7,1): 1 (7,2): bf +""" + + block: # test with 1 column + var ret = "12\nasdfa\nadf" + assertEquals alignTable(ret), """ +12 +asdfa +adf """ + + block: # test with empty input + var ret = "" + assertEquals alignTable(ret), "" + + block: # test with 1 row + var ret = "abc\tdef" + assertEquals alignTable(ret), """ +abc def""" + + block: # test with 1 row ending in \t + var ret = "abc\tdef\t" + assertEquals alignTable(ret), """ +abc def """ + + block: # test with 1 row starting with \t + var ret = "\tabc\tdef\t" + assertEquals alignTable(ret), """ + abc def """ + + + block: # test with variable number of cols per row + var ret = """ +a1,a2,a3 + +b1 +c1,c2 +,d1 +""" + assertEquals alignTableCustom(ret, delim = ',', sep = ","), + """ +(0,0): a1,(0,1): a2,(0,2): a3 +(1,0): ,(1,1): ,(1,2): +(2,0): b1,(2,1): ,(2,2): +(3,0): c1,(3,1): c2,(3,2): +(4,0): ,(4,1): d1,(4,2): +""" + +testAlignTable() diff --git a/tests/compiler/tunittest_light.nim b/tests/compiler/tunittest_light.nim new file mode 100644 index 000000000..d20293d5b --- /dev/null +++ b/tests/compiler/tunittest_light.nim @@ -0,0 +1,55 @@ +import compiler/unittest_light + +proc testAssertEquals() = + assertEquals("foo", "foo") + doAssertRaises(AssertionError): + assertEquals("foo", "foo ") + +proc testMismatch() = + assertEquals(1+1, 2*1) + + let a = """ + some test with space at the end of lines + + can be hard to spot differences when diffing in a terminal + without this helper function + +""" + + let b = """ + some test with space at the end of lines + + can be hard to spot differences when diffing in a terminal + without this helper function + +""" + + doAssert mismatch(a, b) == """ + +lhs:{ + some test with space at the end of lines \n +\n + can be hard to spot differences when diffing in a terminal \n + without this helper function\n +\n +} +rhs:{ + some test with space at the end of lines \n +\n + can be hard to spot differences when diffing in a terminal \n + without this helper function\n +\n +} +lhs.len: 144 rhs.len: 143 +first mismatch index: 110 +lhs[i]: {" "} rhs[i]: {"\n"}lhs[0..<i]:{ + some test with space at the end of lines \n +\n + can be hard to spot differences when diffing in a terminal } +rhs[0..<i]:{ + some test with space at the end of lines \n +\n + can be hard to spot differences when diffing in a terminal }""" + +testMismatch() +testAssertEquals() |