1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 // This file contains the mechanism to "linkify" html source
8 // text containing EBNF sections (as found in go_spec.html).
9 // The result is the input source text with the EBNF sections
10 // modified such that identifiers are linked to the respective
20 type ebnfParser struct {
21 out io.Writer // parser output
22 src []byte // parser input
23 scanner scanner.Scanner
24 prev int // offset of previous token
25 pos int // offset of current token
26 tok rune // one token look-ahead
27 lit string // token literal
30 func (p *ebnfParser) flush() {
31 p.out.Write(p.src[p.prev:p.pos])
35 func (p *ebnfParser) next() {
36 p.tok = p.scanner.Scan()
37 p.pos = p.scanner.Position.Offset
38 p.lit = p.scanner.TokenText()
41 func (p *ebnfParser) printf(format string, args ...interface{}) {
43 fmt.Fprintf(p.out, format, args...)
46 func (p *ebnfParser) errorExpected(msg string) {
47 p.printf(`<span class="highlight">error: expected %s, found %s</span>`, msg, scanner.TokenString(p.tok))
50 func (p *ebnfParser) expect(tok rune) {
52 p.errorExpected(scanner.TokenString(tok))
54 p.next() // make progress in any case
57 func (p *ebnfParser) parseIdentifier(def bool) {
58 if p.tok == scanner.Ident {
61 p.printf(`<a id="%s">%s</a>`, name, name)
63 p.printf(`<a href="#%s" class="noline">%s</a>`, name, name)
65 p.prev += len(name) // skip identifier when printing next time
68 p.expect(scanner.Ident)
72 func (p *ebnfParser) parseTerm() bool {
75 p.parseIdentifier(false)
77 case scanner.String, scanner.RawString:
79 const ellipsis = '…' // U+2026, the horizontal ellipsis character
80 if p.tok == ellipsis {
82 p.expect(scanner.String)
101 return false // no term found
107 func (p *ebnfParser) parseSequence() {
109 p.errorExpected("term")
115 func (p *ebnfParser) parseExpression() {
125 func (p *ebnfParser) parseProduction() {
126 p.parseIdentifier(true)
134 func (p *ebnfParser) parse(out io.Writer, src []byte) {
135 // initialize ebnfParser
138 p.scanner.Init(bytes.NewBuffer(src))
139 p.next() // initializes pos, tok, lit
142 for p.tok != scanner.EOF {
148 // Markers around EBNF sections
150 openTag = []byte(`<pre class="ebnf">`)
151 closeTag = []byte(`</pre>`)
154 func Linkify(out io.Writer, src []byte) {
156 // i: beginning of EBNF text (or end of source)
157 i := bytes.Index(src, openTag)
159 i = len(src) - len(openTag)
163 // j: end of EBNF text (or end of source)
164 j := bytes.Index(src[i:], closeTag) // close marker
170 // write text before EBNF
174 p.parse(out, src[i:j])