1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
package org
import (
"regexp"
"strconv"
"strings"
"unicode/utf8"
)
type Table struct {
Rows []Row
ColumnInfos []ColumnInfo
SeparatorIndices []int
}
type Row struct {
Columns []Column
IsSpecial bool
}
type Column struct {
Children []Node
*ColumnInfo
}
type ColumnInfo struct {
Align string
Len int
DisplayLen int
}
var tableSeparatorRegexp = regexp.MustCompile(`^(\s*)(\|[+-|]*)\s*$`)
var tableRowRegexp = regexp.MustCompile(`^(\s*)(\|.*)`)
var columnAlignAndLengthRegexp = regexp.MustCompile(`^<(l|c|r)?(\d+)?>$`)
func lexTable(line string) (token, bool) {
if m := tableSeparatorRegexp.FindStringSubmatch(line); m != nil {
return token{"tableSeparator", len(m[1]), m[2], m}, true
} else if m := tableRowRegexp.FindStringSubmatch(line); m != nil {
return token{"tableRow", len(m[1]), m[2], m}, true
}
return nilToken, false
}
func (d *Document) parseTable(i int, parentStop stopFn) (int, Node) {
rawRows, separatorIndices, start := [][]string{}, []int{}, i
for ; !parentStop(d, i); i++ {
if t := d.tokens[i]; t.kind == "tableRow" {
rawRow := strings.FieldsFunc(d.tokens[i].content, func(r rune) bool { return r == '|' })
for i := range rawRow {
rawRow[i] = strings.TrimSpace(rawRow[i])
}
rawRows = append(rawRows, rawRow)
} else if t.kind == "tableSeparator" {
separatorIndices = append(separatorIndices, i-start)
rawRows = append(rawRows, nil)
} else {
break
}
}
table := Table{nil, getColumnInfos(rawRows), separatorIndices}
for _, rawColumns := range rawRows {
row := Row{nil, isSpecialRow(rawColumns)}
if len(rawColumns) != 0 {
for i := range table.ColumnInfos {
column := Column{nil, &table.ColumnInfos[i]}
if i < len(rawColumns) {
column.Children = d.parseInline(rawColumns[i])
}
row.Columns = append(row.Columns, column)
}
}
table.Rows = append(table.Rows, row)
}
return i - start, table
}
func getColumnInfos(rows [][]string) []ColumnInfo {
columnCount := 0
for _, columns := range rows {
if n := len(columns); n > columnCount {
columnCount = n
}
}
columnInfos := make([]ColumnInfo, columnCount)
for i := 0; i < columnCount; i++ {
countNumeric, countNonNumeric := 0, 0
for _, columns := range rows {
if i >= len(columns) {
continue
}
if n := utf8.RuneCountInString(columns[i]); n > columnInfos[i].Len {
columnInfos[i].Len = n
}
if m := columnAlignAndLengthRegexp.FindStringSubmatch(columns[i]); m != nil && isSpecialRow(columns) {
switch m[1] {
case "l":
columnInfos[i].Align = "left"
case "c":
columnInfos[i].Align = "center"
case "r":
columnInfos[i].Align = "right"
}
if m[2] != "" {
l, _ := strconv.Atoi(m[2])
columnInfos[i].DisplayLen = l
}
} else if _, err := strconv.ParseFloat(columns[i], 32); err == nil {
countNumeric++
} else if strings.TrimSpace(columns[i]) != "" {
countNonNumeric++
}
}
if columnInfos[i].Align == "" && countNumeric >= countNonNumeric {
columnInfos[i].Align = "right"
}
}
return columnInfos
}
func isSpecialRow(rawColumns []string) bool {
isAlignRow := true
for _, rawColumn := range rawColumns {
if !columnAlignAndLengthRegexp.MatchString(rawColumn) && rawColumn != "" {
isAlignRow = false
}
}
return isAlignRow
}
func (n Table) String() string { return String(n) }
|