package wikilink import ( "fmt" "strings" "unicode/utf8" ) const ( eof rune = -1 ) const ( openLink = "[[" closeLink = "]]" alias = "|" blockRef = "#^" ) func lex(name, input string) *lexer { l := &lexer{ name: name, input: input, state: lexText, items: make(chan item, 2), } go l.run() return l } func (l *lexer) nextItem() item { for { select { case item := <-l.items: return item default: l.state = l.state(l) } } } func (l *lexer) ignore() { l.start = l.pos } func (l *lexer) backup() { l.pos -= l.width } type lexer struct { name, input string start, pos, width int state stateFn items chan item } func (l *lexer) peek() rune { r := l.next() l.backup() return r } func (l *lexer) accept(valid string) bool { if strings.IndexRune(valid, l.next()) >= 0 { return true } l.backup() return false } func (l *lexer) acceptRun(valid string) { for strings.IndexRune(valid, l.next()) >= 0 { } l.backup() } func (l *lexer) emit(t itemType) { l.items <- item{t, l.input[l.start:l.pos]} l.start = l.pos } func (l *lexer) errorf(format string, args ...interface{}) stateFn { l.items <- item{ itemError, fmt.Sprintf(format, args...), } return nil } func lexFragment(l *lexer) stateFn { return l.errorf("lexFragment not implemented") } func lexAlias(l *lexer) stateFn { return l.errorf("lexAlias not implemented") } func lexInsideLink(l *lexer) stateFn { for { if strings.HasPrefix(l.input[l.pos:], closeLink) { return lexCloseLink } r := l.next() switch { case r == eof: case r == '\n': return l.errorf("unclosed link") case r == '#': return lexFragment case r == '|': return lexAlias } } } func lexOpenLink(l *lexer) stateFn { l.pos += len(openLink) l.emit(itemOpenLink) return lexInsideLink } func lexCloseLink(l *lexer) stateFn { l.pos += len(closeLink) l.emit(itemCloseLink) return lexText } func lexText(l *lexer) stateFn { for { if strings.HasPrefix(l.input[l.pos:], openLink) { if l.pos > l.start { l.emit(itemText) } return lexOpenLink } if l.next() == eof { break } if l.pos > l.start { l.emit(itemText) } l.emit(itemEOF) return nil } return nil } func (l *lexer) next() rune { var r rune if l.pos >= len(l.input) { l.width = 0 return eof } r, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) l.pos += l.width return r } func (l *lexer) run() { for state := lexText; state != nil; { state = state(l) } close(l.items) } type stateFn func(*lexer) stateFn type itemType int const ( itemError itemType = iota itemEOF itemText itemOpenLink itemCloseLink itemBlockRef itemAlias ) type item struct { typ itemType val string } func (i item) String() string { switch i.typ { case itemEOF: return "EOF" case itemError: return i.val } if len(i.val) > 10 { return fmt.Sprintf("%.10q...", i.val) } return fmt.Sprintf("%q...", i.val) }