| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- /*
- Tideland Common Go Library - Simple Markup Language
- Copyright (C) 2011 Frank Mueller / Oldenburg / Germany
- Redistribution and use in source and binary forms, with or
- modification, are permitted provided that the following conditions are
- met:
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- Neither the name of Tideland nor the names of its contributors may be
- used to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- THE POSSIBILITY OF SUCH DAMAGE.
- */
- package cgl
- //--------------------
- // IMPORTS
- //--------------------
- import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "os"
- "strings"
- "unicode"
- )
- //--------------------
- // PROCESSOR
- //--------------------
- // Processor interface.
- type Processor interface {
- OpenTag(tag []string)
- CloseTag(tag []string)
- Text(text string)
- }
- //--------------------
- // NODE
- //--------------------
- // The node.
- type Node interface {
- Len() int
- ProcessWith(p Processor)
- }
- //--------------------
- // TAG NODE
- //--------------------
- // The tag node.
- type TagNode struct {
- tag []string
- children []Node
- }
- // Create a new tag node.
- func NewTagNode(tag string) *TagNode {
- tmp := strings.ToLower(tag)
- if !validIdentifier(tmp) {
- return nil
- }
- tn := &TagNode{
- tag: strings.Split(tmp, ":"),
- children: make([]Node, 0),
- }
- return tn
- }
- // Append a new tag.
- func (tn *TagNode) AppendTag(tag string) *TagNode {
- n := NewTagNode(tag)
- if n != nil {
- tn.children = append(tn.children, n)
- }
- return n
- }
- // Append a tag node.
- func (tn *TagNode) AppendTagNode(n *TagNode) *TagNode {
- tn.children = append(tn.children, n)
- return n
- }
- // Append a text node.
- func (tn *TagNode) AppendText(text string) *TextNode {
- n := NewTextNode(text)
- tn.children = append(tn.children, n)
- return n
- }
- // Append a tagged text node.
- func (tn *TagNode) AppendTaggedText(tag, text string) *TagNode {
- n := NewTagNode(tag)
- if n != nil {
- n.AppendText(text)
- tn.children = append(tn.children, n)
- }
- return n
- }
- // Append a text node.
- func (tn *TagNode) AppendTextNode(n *TextNode) *TextNode {
- tn.children = append(tn.children, n)
- return n
- }
- // Return the len of the tag node (aka number of children).
- func (tn *TagNode) Len() int {
- return len(tn.children)
- }
- // Process the node.
- func (tn *TagNode) ProcessWith(p Processor) {
- p.OpenTag(tn.tag)
- for _, child := range tn.children {
- child.ProcessWith(p)
- }
- p.CloseTag(tn.tag)
- }
- // Return the node as a string.
- func (tn *TagNode) String() string {
- buf := bytes.NewBufferString("")
- spp := NewSmlWriterProcessor(buf, true)
- tn.ProcessWith(spp)
- return buf.String()
- }
- //--------------------
- // TEXT NODE
- //--------------------
- // The text node.
- type TextNode struct {
- text string
- }
- // Create a new text node.
- func NewTextNode(text string) *TextNode {
- return &TextNode{text}
- }
- // Return the len of the text node.
- func (tn *TextNode) Len() int {
- return len(tn.text)
- }
- // Process the node.
- func (tn *TextNode) ProcessWith(p Processor) {
- p.Text(tn.text)
- }
- // Return the node as a string.
- func (tn *TextNode) String() string {
- return tn.text
- }
- //--------------------
- // PRIVATE FUNCTIONS
- //--------------------
- // Check an identifier (tag or id).
- func validIdentifier(id string) bool {
- for _, c := range id {
- if c < 'a' || c > 'z' {
- if c < '0' || c > '9' {
- if c != '-' && c != ':' {
- return false
- }
- }
- }
- }
- return true
- }
- //--------------------
- // SML READER
- //--------------------
- // Control values.
- const (
- ctrlText = iota
- ctrlSpace
- ctrlOpen
- ctrlClose
- ctrlEscape
- ctrlTag
- ctrlEOF
- ctrlInvalid
- )
- // Node read modes.
- const (
- modeInit = iota
- modeTag
- modeText
- )
- // Reader for SML.
- type SmlReader struct {
- reader *bufio.Reader
- index int
- root *TagNode
- error os.Error
- }
- // Create the reader.
- func NewSmlReader(reader io.Reader) *SmlReader {
- // Init the reader.
- sr := &SmlReader{
- reader: bufio.NewReader(reader),
- index: -1,
- }
- node, ctrl := sr.readNode()
- switch ctrl {
- case ctrlClose:
- sr.root = node
- sr.error = nil
- case ctrlEOF:
- msg := fmt.Sprintf("eof too early at index %v", sr.index)
- sr.error = os.NewError(msg)
- case ctrlInvalid:
- msg := fmt.Sprintf("invalid rune at index %v", sr.index)
- sr.error = os.NewError(msg)
- }
- return sr
- }
- // Return the root tag node.
- func (sr *SmlReader) RootTagNode() (*TagNode, os.Error) {
- return sr.root, sr.error
- }
- // Read a node.
- func (sr *SmlReader) readNode() (*TagNode, int) {
- var node *TagNode
- var buffer *bytes.Buffer
- mode := modeInit
- for {
- rune, ctrl := sr.readRune()
- sr.index++
- switch mode {
- case modeInit:
- // Before the first opening bracket.
- switch ctrl {
- case ctrlEOF:
- return nil, ctrlEOF
- case ctrlOpen:
- mode = modeTag
- buffer = bytes.NewBufferString("")
- }
- case modeTag:
- // Reading a tag.
- switch ctrl {
- case ctrlEOF:
- return nil, ctrlEOF
- case ctrlTag:
- buffer.WriteRune(rune)
- case ctrlSpace:
- if buffer.Len() == 0 {
- return nil, ctrlInvalid
- }
- node = NewTagNode(buffer.String())
- buffer = bytes.NewBufferString("")
- mode = modeText
- case ctrlClose:
- if buffer.Len() == 0 {
- return nil, ctrlInvalid
- }
- node = NewTagNode(buffer.String())
- return node, ctrlClose
- default:
- return nil, ctrlInvalid
- }
- case modeText:
- // Reading the text including the subnodes following
- // the space after the tag or id.
- switch ctrl {
- case ctrlEOF:
- return nil, ctrlEOF
- case ctrlOpen:
- text := strings.TrimSpace(buffer.String())
- if len(text) > 0 {
- node.AppendText(text)
- }
- buffer = bytes.NewBufferString("")
- sr.reader.UnreadRune()
- subnode, subctrl := sr.readNode()
- if subctrl == ctrlClose {
- // Correct closed subnode.
- node.AppendTagNode(subnode)
- } else {
- // Error while reading the subnode.
- return nil, subctrl
- }
- case ctrlClose:
- text := strings.TrimSpace(buffer.String())
- if len(text) > 0 {
- node.AppendText(text)
- }
- return node, ctrlClose
- case ctrlEscape:
- rune, ctrl = sr.readRune()
- if ctrl == ctrlOpen || ctrl == ctrlClose || ctrl == ctrlEscape {
- buffer.WriteRune(rune)
- sr.index++
- } else {
- return nil, ctrlInvalid
- }
- default:
- buffer.WriteRune(rune)
- }
- }
- }
- return nil, ctrlEOF
- }
- // Read a rune.
- func (sr *SmlReader) readRune() (rune, control int) {
- var size int
- rune, size, sr.error = sr.reader.ReadRune()
- switch {
- case size == 0:
- return rune, ctrlEOF
- case rune == '{':
- return rune, ctrlOpen
- case rune == '}':
- return rune, ctrlClose
- case rune == '^':
- return rune, ctrlEscape
- case rune >= 'a' && rune <= 'z':
- return rune, ctrlTag
- case rune >= 'A' && rune <= 'Z':
- return rune, ctrlTag
- case rune >= '0' && rune <= '9':
- return rune, ctrlTag
- case rune == '-':
- return rune, ctrlTag
- case rune == ':':
- return rune, ctrlTag
- case unicode.IsSpace(rune):
- return rune, ctrlSpace
- }
- return rune, ctrlText
- }
- //--------------------
- // SML WRITER PROCESSOR
- //--------------------
- // Processor for writing SML.
- type SmlWriterProcessor struct {
- writer *bufio.Writer
- prettyPrint bool
- indentLevel int
- }
- // Create a new SML writer processor.
- func NewSmlWriterProcessor(writer io.Writer, prettyPrint bool) *SmlWriterProcessor {
- swp := &SmlWriterProcessor{
- writer: bufio.NewWriter(writer),
- prettyPrint: prettyPrint,
- indentLevel: 0,
- }
- return swp
- }
- // Open a tag.
- func (swp *SmlWriterProcessor) OpenTag(tag []string) {
- swp.writeIndent(true)
- swp.writer.WriteString("{")
- swp.writer.WriteString(strings.Join(tag, ":"))
- }
- // Close a tag.
- func (swp *SmlWriterProcessor) CloseTag(tag []string) {
- swp.writer.WriteString("}")
- if swp.prettyPrint {
- swp.indentLevel--
- }
- swp.writer.Flush()
- }
- // Write a text.
- func (swp *SmlWriterProcessor) Text(text string) {
- ta := strings.Replace(text, "^", "^^", -1)
- tb := strings.Replace(ta, "{", "^{", -1)
- tc := strings.Replace(tb, "}", "^}", -1)
- swp.writeIndent(false)
- swp.writer.WriteString(tc)
- }
- // Write an indent in case of pretty print.
- func (swp *SmlWriterProcessor) writeIndent(increase bool) {
- if swp.prettyPrint {
- if swp.indentLevel > 0 {
- swp.writer.WriteString("\n")
- }
- for i := 0; i < swp.indentLevel; i++ {
- swp.writer.WriteString("\t")
- }
- if increase {
- swp.indentLevel++
- }
- } else {
- swp.writer.WriteString(" ")
- }
- }
- /*
- EOF
- */
|