summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2019-01-09 00:46:44 -0800
committerAndreas Rumpf <rumpf_a@web.de>2019-01-09 09:46:44 +0100
commit23c1ee982e2d3795001879a4527581f33875cd33 (patch)
tree799acb5b12ca6f24796213a54d4ed9baf6d70beb
parent258952832f312a25d0ab9771237a547297b336b8 (diff)
downloadNim-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.nim83
-rw-r--r--compiler/unittest_light.nim37
-rw-r--r--tests/compiler/nim.cfg7
-rw-r--r--tests/compiler/tasciitables.nim109
-rw-r--r--tests/compiler/tunittest_light.nim55
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()