/* * Copyright (C) 2022 Brian Evans * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package cmdparse import ( "fmt" "io" ) //------------------------------------------------\\ // + + + T Y P E S + + + \\ //--------------------------------------------------\\ type Parser struct { s *scanner buffer struct { token Token size int } } type Command struct { Action string Target string Value []string Type Comtype } type Comtype int const ( GOURL Comtype = iota GOLINK SIMPLE DOLINK DOLINKAS DOAS DO ) //------------------------------------------------\\ // + + + R E C E I V E R S + + + \\ //--------------------------------------------------\\ func (p *Parser) scan() (current Token) { if p.buffer.size != 0 { p.buffer.size = 0 return p.buffer.token } current = p.s.scan() for { if current.kind != Whitespace { break } current = p.s.scan() } p.buffer.token = current return } func (p *Parser) unscan() { p.buffer.size = 1 } func (p *Parser) parseNonAction() (*Command, error) { p.unscan() t := p.scan() cm := &Command{} if t.kind == Value { cm.Target = t.val cm.Type = GOLINK } else if t.kind == Word { cm.Target = t.val cm.Type = GOURL } else { return nil, fmt.Errorf("Found %q, expected action, url, or link number", t.val) } if u := p.scan(); u.kind != End { return nil, fmt.Errorf("Found %q, expected EOF", u.val) } return cm, nil } func (p *Parser) parseAction() (*Command, error) { p.unscan() t := p.scan() cm := &Command{} cm.Action = t.val t = p.scan() switch t.kind { case End: cm.Type = SIMPLE return cm, nil case Value: cm.Target = t.val cm.Type = DOLINK case Word, Action: cm.Value = append(cm.Value, t.val) cm.Type = DO case Whitespace: return nil, fmt.Errorf("Found %q (%d), expected value", t.val, t.kind) } t = p.scan() if t.kind == End { return cm, nil } else { if cm.Type == DOLINK { cm.Type = DOLINKAS } else { cm.Type = DOAS } cm.Value = append(cm.Value, t.val) for { token := p.scan() if token.kind == End { break } else if token.kind == Whitespace { continue } cm.Value = append(cm.Value, token.val) } } return cm, nil } func (p *Parser) Parse() (*Command, error) { if t := p.scan(); t.kind != Action { return p.parseNonAction() } else { return p.parseAction() } } //------------------------------------------------\\ // + + + F U N C T I O N S + + + \\ //--------------------------------------------------\\ func NewParser(r io.Reader) *Parser { return &Parser{s: NewScanner(r)} }