Source File
parser.go
Belonging Package
github.com/yuin/goldmark/parser
package parser
import (
)
String() string
Label() []byte
Destination() []byte
func (, , []byte) Reference {
return &reference{, , }
}
func ( *reference) () []byte {
return .label
}
func ( *reference) () []byte {
return .destination
}
func ( *reference) () []byte {
return .title
}
func ( *reference) () string {
return fmt.Sprintf("Reference{Label:%s, Destination:%s, Title:%s}", .label, .destination, .title)
}
Put(value []byte)
}
type ids struct {
values map[string]bool
}
func () IDs {
return &ids{
values: map[string]bool{},
}
}
func ( *ids) ( []byte, ast.NodeKind) []byte {
= util.TrimLeftSpace()
= util.TrimRightSpace()
:= []byte{}
for := 0; < len(); {
:= []
:= util.UTF8Len()
+= int()
if != 1 {
continue
}
if util.IsAlphaNumeric() {
if 'A' <= && <= 'Z' {
+= 'a' - 'A'
}
= append(, )
} else if util.IsSpace() || == '-' || == '_' {
= append(, '-')
}
}
if len() == 0 {
if == ast.KindHeading {
= []byte("heading")
} else {
= []byte("id")
}
}
if , := .values[util.BytesToReadOnlyString()]; ! {
.values[util.BytesToReadOnlyString()] = true
return
}
for := 1; ; ++ {
:= fmt.Sprintf("%s-%d", , )
if , := .values[]; ! {
.values[] = true
return []byte()
}
}
}
func ( *ids) ( []byte) {
.values[util.BytesToReadOnlyString()] = true
}
type ContextKey int
func () ContextKey {
ContextKeyMax++
return ContextKeyMax
}
String() string
Get(ContextKey) interface{}
ComputeIfAbsent(ContextKey, func() interface{}) interface{}
Set(ContextKey, interface{})
AddReference(Reference)
References() []Reference
IDs() IDs
BlockOffset() int
SetBlockOffset(int)
BlockIndent() int
SetBlockIndent(int)
FirstDelimiter() *Delimiter
LastDelimiter() *Delimiter
PushDelimiter(delimiter *Delimiter)
RemoveDelimiter(d *Delimiter)
OpenedBlocks() []Block
SetOpenedBlocks([]Block)
LastOpenedBlock() Block
IsInLinkLabel() bool
}
type ContextConfig struct {
IDs IDs
}
type ContextOption func(*ContextConfig)
func ( IDs) ContextOption {
return func( *ContextConfig) {
.IDs =
}
}
type parseContext struct {
store []interface{}
ids IDs
refs map[string]Reference
blockOffset int
blockIndent int
delimiters *Delimiter
lastDelimiter *Delimiter
openedBlocks []Block
}
func ( ...ContextOption) Context {
:= &ContextConfig{
IDs: newIDs(),
}
for , := range {
()
}
return &parseContext{
store: make([]interface{}, ContextKeyMax+1),
refs: map[string]Reference{},
ids: .IDs,
blockOffset: -1,
blockIndent: -1,
delimiters: nil,
lastDelimiter: nil,
openedBlocks: []Block{},
}
}
func ( *parseContext) ( ContextKey) interface{} {
return .store[]
}
func ( *parseContext) ( ContextKey, func() interface{}) interface{} {
:= .store[]
if == nil {
= ()
.store[] =
}
return
}
func ( *parseContext) ( ContextKey, interface{}) {
.store[] =
}
func ( *parseContext) () IDs {
return .ids
}
func ( *parseContext) () int {
return .blockOffset
}
func ( *parseContext) ( int) {
.blockOffset =
}
func ( *parseContext) () int {
return .blockIndent
}
func ( *parseContext) ( int) {
.blockIndent =
}
func ( *parseContext) () *Delimiter {
return .lastDelimiter
}
func ( *parseContext) () *Delimiter {
return .delimiters
}
func ( *parseContext) ( *Delimiter) {
if .delimiters == nil {
.delimiters =
.lastDelimiter =
} else {
:= .lastDelimiter
.lastDelimiter =
.NextDelimiter =
.PreviousDelimiter =
}
}
func ( *parseContext) ( *Delimiter) {
if .PreviousDelimiter == nil {
.delimiters = .NextDelimiter
} else {
.PreviousDelimiter.NextDelimiter = .NextDelimiter
if .NextDelimiter != nil {
.NextDelimiter.PreviousDelimiter = .PreviousDelimiter
}
}
if .NextDelimiter == nil {
.lastDelimiter = .PreviousDelimiter
}
if .delimiters != nil {
.delimiters.PreviousDelimiter = nil
}
if .lastDelimiter != nil {
.lastDelimiter.NextDelimiter = nil
}
.NextDelimiter = nil
.PreviousDelimiter = nil
if .Length != 0 {
ast.MergeOrReplaceTextSegment(.Parent(), , .Segment)
} else {
.Parent().RemoveChild(.Parent(), )
}
}
func ( *parseContext) ( ast.Node) {
if .lastDelimiter == nil {
return
}
var ast.Node
for = .lastDelimiter; != nil && != ; {
:= .PreviousSibling()
if , := .(*Delimiter); {
.RemoveDelimiter()
}
=
}
}
func ( *parseContext) ( Reference) {
:= util.ToLinkReference(.Label())
if , := .refs[]; ! {
.refs[] =
}
}
func ( *parseContext) ( string) (Reference, bool) {
, := .refs[]
return ,
}
func ( *parseContext) () []Reference {
:= make([]Reference, 0, len(.refs))
for , := range .refs {
= append(, )
}
return
}
func ( *parseContext) () string {
:= []string{}
for , := range .refs {
= append(, .String())
}
return fmt.Sprintf("Context{Store:%#v, Refs:%s}", .store, strings.Join(, ","))
}
func ( *parseContext) () []Block {
return .openedBlocks
}
func ( *parseContext) ( []Block) {
.openedBlocks =
}
func ( *parseContext) () Block {
if := len(.openedBlocks); != 0 {
return .openedBlocks[-1]
}
return Block{}
}
func ( *parseContext) () bool {
:= .Get(linkLabelStateKey)
return != nil
}
type Config struct {
Options map[OptionName]interface{}
BlockParsers util.PrioritizedSlice /*<BlockParser>*/
InlineParsers util.PrioritizedSlice /*<InlineParser>*/
ParagraphTransformers util.PrioritizedSlice /*<ParagraphTransformer>*/
ASTTransformers util.PrioritizedSlice /*<ASTTransformer>*/
}
func () *Config {
return &Config{
Options: map[OptionName]interface{}{},
BlockParsers: util.PrioritizedSlice{},
InlineParsers: util.PrioritizedSlice{},
ParagraphTransformers: util.PrioritizedSlice{},
ASTTransformers: util.PrioritizedSlice{},
}
}
type OptionName string
const optAttribute OptionName = "Attribute"
type withAttribute struct {
}
func ( *withAttribute) ( *Config) {
.Options[optAttribute] = true
}
func () Option {
return &withAttribute{}
}
Parse(reader text.Reader, opts ...ParseOption) ast.Node
AddOptions(...Option)
}
SetOption(name OptionName, value interface{})
}
Trigger() []byte
CanInterruptParagraph() bool
CanAcceptIndentedLine() bool
}
Trigger() []byte
func () []util.PrioritizedValue {
return []util.PrioritizedValue{
util.Prioritized(NewSetextHeadingParser(), 100),
util.Prioritized(NewThematicBreakParser(), 200),
util.Prioritized(NewListParser(), 300),
util.Prioritized(NewListItemParser(), 400),
util.Prioritized(NewCodeBlockParser(), 500),
util.Prioritized(NewATXHeadingParser(), 600),
util.Prioritized(NewFencedCodeBlockParser(), 700),
util.Prioritized(NewBlockquoteParser(), 800),
util.Prioritized(NewHTMLBlockParser(), 900),
util.Prioritized(NewParagraphParser(), 1000),
}
}
func () []util.PrioritizedValue {
return []util.PrioritizedValue{
util.Prioritized(NewCodeSpanParser(), 100),
util.Prioritized(NewLinkParser(), 200),
util.Prioritized(NewAutoLinkParser(), 300),
util.Prioritized(NewRawHTMLParser(), 400),
util.Prioritized(NewEmphasisParser(), 500),
}
}
func () []util.PrioritizedValue {
return []util.PrioritizedValue{
util.Prioritized(LinkReferenceParagraphTransformer, 100),
}
}
Parser BlockParser
}
type parser struct {
options map[OptionName]interface{}
blockParsers [256][]BlockParser
freeBlockParsers []BlockParser
inlineParsers [256][]InlineParser
closeBlockers []CloseBlocker
paragraphTransformers []ParagraphTransformer
astTransformers []ASTTransformer
config *Config
initSync sync.Once
}
type withBlockParsers struct {
value []util.PrioritizedValue
}
func ( *withBlockParsers) ( *Config) {
.BlockParsers = append(.BlockParsers, .value...)
}
func ( ...util.PrioritizedValue) Option {
return &withBlockParsers{}
}
type withInlineParsers struct {
value []util.PrioritizedValue
}
func ( *withInlineParsers) ( *Config) {
.InlineParsers = append(.InlineParsers, .value...)
}
func ( ...util.PrioritizedValue) Option {
return &withInlineParsers{}
}
type withParagraphTransformers struct {
value []util.PrioritizedValue
}
func ( *withParagraphTransformers) ( *Config) {
.ParagraphTransformers = append(.ParagraphTransformers, .value...)
}
func ( ...util.PrioritizedValue) Option {
return &withParagraphTransformers{}
}
type withASTTransformers struct {
value []util.PrioritizedValue
}
func ( *withASTTransformers) ( *Config) {
.ASTTransformers = append(.ASTTransformers, .value...)
}
func ( ...util.PrioritizedValue) Option {
return &withASTTransformers{}
}
type withOption struct {
name OptionName
value interface{}
}
func ( *withOption) ( *Config) {
.Options[.name] = .value
}
func ( OptionName, interface{}) Option {
return &withOption{, }
}
func ( ...Option) Parser {
:= NewConfig()
for , := range {
.SetParserOption()
}
:= &parser{
options: map[OptionName]interface{}{},
config: ,
}
return
}
func ( *parser) ( ...Option) {
for , := range {
.SetParserOption(.config)
}
}
func ( *parser) ( util.PrioritizedValue, map[OptionName]interface{}) {
, := .Value.(BlockParser)
if ! {
panic(fmt.Sprintf("%v is not a BlockParser", .Value))
}
:= .Trigger()
, := .Value.(SetOptioner)
if {
for , := range {
.SetOption(, )
}
}
if == nil {
.freeBlockParsers = append(.freeBlockParsers, )
} else {
for , := range {
if .blockParsers[] == nil {
.blockParsers[] = []BlockParser{}
}
.blockParsers[] = append(.blockParsers[], )
}
}
}
func ( *parser) ( util.PrioritizedValue, map[OptionName]interface{}) {
, := .Value.(InlineParser)
if ! {
panic(fmt.Sprintf("%v is not a InlineParser", .Value))
}
:= .Trigger()
, := .Value.(SetOptioner)
if {
for , := range {
.SetOption(, )
}
}
if , := .(CloseBlocker); {
.closeBlockers = append(.closeBlockers, )
}
for , := range {
if .inlineParsers[] == nil {
.inlineParsers[] = []InlineParser{}
}
.inlineParsers[] = append(.inlineParsers[], )
}
}
func ( *parser) ( util.PrioritizedValue, map[OptionName]interface{}) {
, := .Value.(ParagraphTransformer)
if ! {
panic(fmt.Sprintf("%v is not a ParagraphTransformer", .Value))
}
, := .Value.(SetOptioner)
if {
for , := range {
.SetOption(, )
}
}
.paragraphTransformers = append(.paragraphTransformers, )
}
func ( *parser) ( util.PrioritizedValue, map[OptionName]interface{}) {
, := .Value.(ASTTransformer)
if ! {
panic(fmt.Sprintf("%v is not a ASTTransformer", .Value))
}
, := .Value.(SetOptioner)
if {
for , := range {
.SetOption(, )
}
}
.astTransformers = append(.astTransformers, )
}
type ParseConfig struct {
Context Context
}
type ParseOption func(c *ParseConfig)
func ( Context) ParseOption {
return func( *ParseConfig) {
.Context =
}
}
func ( *parser) ( text.Reader, ...ParseOption) ast.Node {
.initSync.Do(func() {
.config.BlockParsers.Sort()
for , := range .config.BlockParsers {
.addBlockParser(, .config.Options)
}
for := range .blockParsers {
if .blockParsers[] != nil {
.blockParsers[] = append(.blockParsers[], .freeBlockParsers...)
}
}
.config.InlineParsers.Sort()
for , := range .config.InlineParsers {
.addInlineParser(, .config.Options)
}
.config.ParagraphTransformers.Sort()
for , := range .config.ParagraphTransformers {
.addParagraphTransformer(, .config.Options)
}
.config.ASTTransformers.Sort()
for , := range .config.ASTTransformers {
.addASTTransformer(, .config.Options)
}
.config = nil
})
:= &ParseConfig{}
for , := range {
()
}
if .Context == nil {
.Context = NewContext()
}
:= .Context
:= ast.NewDocument()
.parseBlocks(, , )
:= text.NewBlockReader(.Source(), nil)
.walkBlock(, func( ast.Node) {
.parseBlock(, , )
})
for , := range .astTransformers {
.Transform(, , )
return
}
func ( *parser) ( *ast.Paragraph, text.Reader, Context) bool {
for , := range .paragraphTransformers {
.Transform(, , )
if .Parent() == nil {
return true
}
}
return false
}
func ( *parser) (, int, text.Reader, Context) {
:= .OpenedBlocks()
for := ; >= ; -- {
:= [].Node
[].Parser.Close([].Node, , )
, := .(*ast.Paragraph)
if && .Parent() != nil {
.transformParagraph(, , )
}
}
if == len()-1 {
= [0:]
} else {
= append([0:], [+1:]...)
}
.SetOpenedBlocks()
}
type blockOpenResult int
const (
paragraphContinuation blockOpenResult = iota + 1
newBlocksOpened
noBlocksOpened
)
func ( *parser) ( ast.Node, bool, text.Reader, Context) blockOpenResult {
:= blockOpenResult(noBlocksOpened)
:= false
:= .LastOpenedBlock()
if .Node != nil {
= ast.IsParagraph(.Node)
}
:
var []BlockParser
, := .PeekLine()
, := util.IndentWidth(, .LineOffset())
if >= len() {
.SetBlockOffset(-1)
.SetBlockIndent(-1)
} else {
.SetBlockOffset()
.SetBlockIndent()
}
if == nil || [0] == '\n' {
goto
}
= .freeBlockParsers
if < len() {
= .blockParsers[[]]
if == nil {
= .freeBlockParsers
}
}
if == nil {
goto
}
for , := range {
if && == noBlocksOpened && !.CanInterruptParagraph() {
continue
}
if > 3 && !.CanAcceptIndentedLine() {
continue
}
= .LastOpenedBlock()
:= .Node
, := .Open(, , )
if &RequireParagraph != 0 {
.Parser.Close(, , )
:= .OpenedBlocks()
.SetOpenedBlocks([0 : len()-1])
= false
goto
}
}
}
.SetBlankPreviousLines()
if != nil && .Parent() == nil {
:= len(.OpenedBlocks()) - 1
.closeBlocks(, , , )
}
.AppendChild(, )
= newBlocksOpened
:= Block{, }
.SetOpenedBlocks(append(.OpenedBlocks(), ))
if &HasChildren != 0 {
=
goto // try child block
}
break // no children, can not open more blocks on this line
}
}
:
if == noBlocksOpened && {
:= .Parser.Continue(.Node, , )
if &Continue != 0 {
= paragraphContinuation
}
}
return
}
type lineStat struct {
lineNum int
level int
isBlank bool
}
func (, int, []lineStat) bool {
:= true
for := len() - 1 - ; >= 0; -- {
= false
:= []
if .lineNum == {
if .level < && .isBlank {
return true
} else if .level == {
return .isBlank
}
}
if .lineNum < {
return
}
}
return
}
func ( *parser) ( ast.Node, text.Reader, Context) {
.SetOpenedBlocks([]Block{})
:= make([]lineStat, 0, 128)
:= false
for { // process blocks separated by blank lines
, , := .SkipBlankLines()
if ! {
return
}
, := .Position()
if != 0 {
= [0:0]
:= len(.OpenedBlocks())
for := 0; < ; ++ {
= append(, lineStat{ - 1, , != 0})
}
}
if .openBlocks(, , , ) != newBlocksOpened {
return
}
.AdvanceLine()
for { // process opened blocks line by line
:= .OpenedBlocks()
:= len()
if == 0 {
break
}
:= - 1
for := 0; < ; ++ {
:= []
, := .PeekLine()
if == nil {
.closeBlocks(, 0, , )
.AdvanceLine()
return
}
, := .Position()
if &HasChildren != 0 && == {
= isBlankLine(-1, , )
.openBlocks(.Node, , , )
break
}
continue
}
= isBlankLine(-1, , )
:=
if != 0 {
= [-1].Node
}
:= [].Node
:= .openBlocks(, , , )
if [].Node != {
--
}
.closeBlocks(, , , )
}
break
}
.AdvanceLine()
}
}
}
func ( *parser) ( ast.Node, func( ast.Node)) {
for := .FirstChild(); != nil; = .NextSibling() {
.(, )
}
()
}
func ( *parser) ( text.BlockReader, ast.Node, Context) {
if .IsRaw() {
return
}
:= false
:= .Source()
.Reset(.Lines())
for {
:
, := .PeekLine()
if == nil {
break
}
:= len()
:= false
:= [-1] == '\n'
if >= 2 && [-2] == '\\' && { // ends with \\n
-= 2
= true
} else if >= 3 && [-3] == '\\' && [-2] == '\r' && { // ends with \\r\n
-= 3
= true
} else if >= 3 && [-3] == ' ' && [-2] == ' ' && { // ends with [space][space]\n
-= 3
= true
} else if >= 4 && [-4] == ' ' && [-3] == ' ' && [-2] == '\r' && { // ends with [space][space]\r\n
-= 4
= true
}
, := .Position()
:= 0
for := 0; < ; ++ {
:= []
if == '\n' {
break
}
:= util.IsSpace()
:= util.IsPunct()
if ( && !) || || == 0 {
:=
if || ( == 0 && !) {
= ' '
}
:= .inlineParsers[]
if != nil {
.Advance()
= 0
, := .Position()
if != 0 {
, := .Position()
ast.MergeOrAppendTextSegment(, .Between())
_, = .Position()
}
var ast.Node
for , := range {
= .Parse(, , )
if != nil {
break
}
.SetPosition(, )
}
if != nil {
.AppendChild(, )
goto
}
}
}
if {
= false
++
continue
}
if == '\\' {
= true
++
continue
}
= false
++
}
if != 0 {
.Advance()
}
, := .Position()
if != {
continue
}
:= .Between()
:= .Stop
:= .WithStop()
:= ast.NewTextSegment(.TrimRightSpace())
.SetSoftLineBreak()
.SetHardLineBreak()
.AppendChild(, )
.AdvanceLine()
}
ProcessDelimiters(nil, )
for , := range .closeBlockers {
.CloseBlock(, , )
}
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin 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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |