package ini import ( "fmt" "log" "asciigoat.org/core/lexer" "asciigoat.org/ini/parser" ) type token struct { pos lexer.Position typ parser.TokenType value string } func (t token) String() string { return fmt.Sprintf("%s %s: %q", t.pos, t.typ, t.value) } // queueValue extracts the value of element on the queue if the type matches. func (dec *Decoder) queueValue(idx int, typ parser.TokenType) (string, bool) { switch { case idx < 0 || idx >= len(dec.queue): // out of range return "", false case dec.queue[idx].typ != typ: // wrong type return "", false default: // match return dec.queue[idx].value, true } } // queueReset removes all tokens from the queue func (dec *Decoder) queueReset() { dec.queue = dec.queue[:0] } // queueType tells if the specified element on the queue is of the required type. func (dec *Decoder) queueType(idx int, typ parser.TokenType) bool { _, ok := dec.queueValue(idx, typ) return ok } // queueDepth confirms the current depth of the queue func (dec *Decoder) queueDepth(depth int) bool { return len(dec.queue) == depth } // queueDepthType confirms the current depth of the queue and the type of the last // element. func (dec *Decoder) queueDepthType(depth int, typ parser.TokenType) bool { if dec.queueDepth(depth) { return dec.queueType(depth-1, typ) } return false } // typeOK tells if a token of the specified type is acceptable // at this time. func (dec *Decoder) typeOK(typ parser.TokenType) bool { switch typ { case parser.TokenSectionStart: return dec.queueDepth(0) case parser.TokenSectionName: return dec.queueDepthType(1, parser.TokenSectionStart) case parser.TokenSectionSubname: return dec.queueDepthType(2, parser.TokenSectionName) case parser.TokenSectionEnd: return dec.queueType(1, parser.TokenSectionName) case parser.TokenFieldKey: return dec.queueDepth(0) case parser.TokenFieldValue: return dec.queueDepthType(1, parser.TokenFieldKey) case parser.TokenComment: panic("unreachable") default: return false } } // execute is called after each acceptable token is appended to the queue func (dec *Decoder) execute() error { if l := len(dec.queue); l > 0 { // based on the type of the last element switch dec.queue[l-1].typ { case parser.TokenSectionEnd: name1, _ := dec.queueValue(1, parser.TokenSectionName) name2, ok2 := dec.queueValue(2, parser.TokenSectionSubname) defer dec.queueReset() return dec.executeSection(name1, name2, ok2) case parser.TokenFieldValue: key, _ := dec.queueValue(0, parser.TokenFieldKey) value, _ := dec.queueValue(1, parser.TokenFieldValue) defer dec.queueReset() return dec.executeField(key, value) } } return nil } // revive:disable:flag-parameter func (*Decoder) executeSection(key, id string, hasID bool) error { // revive:enable:flag-parameter if hasID { log.Printf("%s: %s%s[%q]: %q", "ini", "", "section", key, id) } else { log.Printf("%s: %s%s[%q]", "ini", "", "section", key) } return nil } func (*Decoder) executeField(key, value string) error { log.Printf("%s: %s%s[%q]: %q", "ini", " ", "field", key, value) return nil } // parserOnToken is the callback from the parser func (dec *Decoder) parserOnToken(pos lexer.Position, typ parser.TokenType, value string) error { var err error t := &token{pos, typ, value} switch { case typ == parser.TokenComment: // ignore comments case dec.typeOK(typ): // acceptable token dec.queue = append(dec.queue, t) err = dec.execute() default: // unacceptable err = dec.newErrInvalidToken(t) } return err }