package parserimport// check if the specified position is preceded by an odd number of backslashesfunc isBackslashEscaped( []byte, int) bool { := 0for --1 >= 0 && [--1] == '\\' { ++ }return &1 == 1}func ( *Parser) ( []byte, []ast.CellAlignFlags, bool) { .AddBlock(&ast.TableRow{}) := 0 := skipChar(, 0, '|') := len() := 0// keep track of total colspan in this row.for = 0; < len() && < ; ++ { := 0 = skipChar(, , ' ') := // If we are in a codespan we should discount any | we see, check for that here and skip ahead.if , := codeSpan(, [:], 0); > 0 { += - 1 }for < && ([] != '|' || isBackslashEscaped(, )) && [] != '\n' { ++ } := // skip the end-of-cell marker, possibly taking us past end of buffer // each _extra_ | means a colspanfor < len() && [] == '|' && !isBackslashEscaped(, ) { ++ ++ }// only colspan > 1 make sense.if < 2 { = 0 }for > && -1 < && [-1] == ' ' { -- } := &ast.TableCell{IsHeader: ,Align: [],ColSpan: , } .Content = [:]if == && > 0 {// an empty cell that we should ignore, it exists because of colspan -- } else { .AddBlock() }if > 0 { += - 1 } }// pad it out with empty columns to get the right numberfor ; < len(); ++ { := &ast.TableCell{IsHeader: ,Align: [], } .AddBlock() }// silently ignore rows with too many cells}// tableFooter parses the (optional) table footer.func ( *Parser) ( []byte) bool { := 1// ignore up to 3 spaces := len() := skipCharN(, 0, ' ', 3)for ; < && [] != '\n'; ++ {// If we are in a codespan we should discount any | we see, check for that here and skip ahead.if , := codeSpan(, [:], 0); > 0 { += - 1 }if [] == '|' && !isBackslashEscaped(, ) { ++continue }// remaining data must be the = characterif [] != '=' {returnfalse } }// doesn't look like a table footerif == 1 {returnfalse } .AddBlock(&ast.TableFooter{})returntrue}// tableHeaders parses the header. If recognized it will also add a table.func ( *Parser) ( []byte, bool) ( int, []ast.CellAlignFlags, ast.Node) { := 0 := 1 := true := truefor = 0; < len() && [] != '\n'; ++ {// If we are in a codespan we should discount any | we see, check for that here and skip ahead.if , := codeSpan(, [:], 0); > 0 { += - 1 }if [] == '|' && !isBackslashEscaped(, ) { ++ }if [] != '-' && [] != ' ' && [] != ':' && [] != '|' { = false }if [] != ' ' && [] != '|' { = false } }// doesn't look like a table headerif == 1 {return }// include the newline in the data sent to tableRow := skipCharN(, , '\n', 1) := [:]// column count ignores pipes at beginning or end of lineif [0] == '|' { -- } { := // remove whitespace from the endforlen() > 0 { := len() - 1if [] == '\n' || [] == ' ' { = [:] } else {break } } := len()if > 2 && [-1] == '|' && !isBackslashEscaped(, -1) { -- } }// if the header looks like a underline, then we omit the header // and parse the first line again as underlineif && ! { = nil = 0 } else { ++ // move past newline } = make([]ast.CellAlignFlags, )// move on to the header underlineif >= len() {return }if [] == '|' && !isBackslashEscaped(, ) { ++ } = skipChar(, , ' ')// each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3 // and trailing | optional on last column := 0 := len()for < && [] != '\n' { := 0if [] == ':' { ++ [] |= ast.TableAlignmentLeft ++ }for < && [] == '-' { ++ ++ }if < && [] == ':' { ++ [] |= ast.TableAlignmentRight ++ }for < && [] == ' ' { ++ }if == {return }// end of column test is messyswitch {case < 1:// not a valid columnreturncase [] == '|' && !isBackslashEscaped(, ):// marker found, now skip past trailing whitespace ++ ++for < && [] == ' ' { ++ }// trailing junk found after last columnif >= && < len() && [] != '\n' {return }case ([] != '|' || isBackslashEscaped(, )) && +1 < :// something else found where marker was requiredreturncase [] == '\n':// marker is optional for the last column ++default:// trailing junk found after last columnreturn } }if != {return }if { = &ast.Table{} .AddBlock()if != nil { .AddBlock(&ast.TableHeader{}) .tableRow(, , true) } } = skipCharN(, , '\n', 1)return}/*Table:Name | Age | Phone------|-----|---------Bob | 31 | 555-1234Alice | 27 | 555-4321*/func ( *Parser) ( []byte) int { , , := .tableHeader(, true)if == 0 {return0 } .AddBlock(&ast.TableBody{})for < len() { , := 0, for ; < len() && [] != '\n'; ++ {if [] == '|' { ++ } }if == 0 { = break }// include the newline in data sent to tableRow = skipCharN(, , '\n', 1)if .tableFooter([:]) {continue } .tableRow([:], , false) }if , , := .caption([:], []byte(captionTable)); > 0 { := &ast.Caption{} .Inline(, )// Some switcheroo to re-insert the parsed table as a child of the captionfigure. := &ast.CaptionFigure{} .HeadingID = := &ast.Table{}// Retain any block level attributes. .AsContainer().Attribute = .AsContainer().Attribute := .GetChildren()ast.RemoveFromTree() .SetChildren()ast.AppendChild(, )ast.AppendChild(, ) .addChild() .Finalize() += }return}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.