package parse import ( "errors" ) var ( // ErrBadSubstitution represents a substitution parsing error. ErrBadSubstitution = errors.New("bad substitution") // ErrMissingClosingBrace represents a missing closing brace "}" error. ErrMissingClosingBrace = errors.New("missing closing brace") // ErrParseVariableName represents the error when unable to parse a // variable name within a substitution. ErrParseVariableName = errors.New("unable to parse variable name") // ErrParseFuncSubstitution represents the error when unable to parse the // substitution within a function parameter. ErrParseFuncSubstitution = errors.New("unable to parse substitution within function") // ErrParseDefaultFunction represent the error when unable to parse a // default function. ErrParseDefaultFunction = errors.New("unable to parse default function") ) // Tree is the representation of a single parsed SQL statement. type Tree struct { Root Node // Parsing only; cleared after parse. scanner *scanner } // Parse parses the string and returns a Tree. func Parse(buf string) (*Tree, error) { t := new(Tree) t.scanner = new(scanner) return t.Parse(buf) } // Parse parses the string buffer to construct an ast // representation for expansion. func (t *Tree) Parse(buf string) (tree *Tree, err error) { t.scanner.init(buf) t.Root, err = t.parseAny() return t, err } func (t *Tree) parseAny() (Node, error) { t.scanner.accept = acceptRune t.scanner.mode = scanIdent | scanLbrack | scanEscape switch t.scanner.scan() { case tokenIdent: left := newTextNode( t.scanner.string(), ) right, err := t.parseAny() switch { case err != nil: return nil, err case right == empty: return left, nil } return newListNode(left, right), nil case tokenEOF: return empty, nil case tokenLbrack: left, err := t.parseFunc() if err != nil { return nil, err } right, err := t.parseAny() switch { case err != nil: return nil, err case right == empty: return left, nil } return newListNode(left, right), nil } return nil, ErrBadSubstitution } func (t *Tree) parseFunc() (Node, error) { switch t.scanner.peek() { case '#': return t.parseLenFunc() } var name string t.scanner.accept = acceptIdent t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: name = t.scanner.string() default: return nil, ErrParseVariableName } switch t.scanner.peek() { case ':': return t.parseDefaultOrSubstr(name) case '=': return t.parseDefaultFunc(name) case ',', '^': return t.parseCasingFunc(name) case '/': return t.parseReplaceFunc(name) case '#': return t.parseRemoveFunc(name, acceptHashFunc) case '%': return t.parseRemoveFunc(name, acceptPercentFunc) } t.scanner.accept = acceptIdent t.scanner.mode = scanRbrack switch t.scanner.scan() { case tokenRbrack: return newFuncNode(name), nil default: return nil, ErrMissingClosingBrace } } // parse a substitution function parameter. func (t *Tree) parseParam(accept acceptFunc, mode byte) (Node, error) { t.scanner.accept = accept t.scanner.mode = mode | scanLbrack switch t.scanner.scan() { case tokenLbrack: return t.parseFunc() case tokenIdent: return newTextNode( t.scanner.string(), ), nil case tokenRbrack: return newTextNode( t.scanner.string(), ), nil default: return nil, ErrParseFuncSubstitution } } // parse either a default or substring substitution function. func (t *Tree) parseDefaultOrSubstr(name string) (Node, error) { t.scanner.read() r := t.scanner.peek() t.scanner.unread() switch r { case '=', '-', '?', '+': return t.parseDefaultFunc(name) default: return t.parseSubstrFunc(name) } } // parses the ${param:offset} string function // parses the ${param:offset:length} string function func (t *Tree) parseSubstrFunc(name string) (Node, error) { node := new(FuncNode) node.Param = name t.scanner.accept = acceptOneColon t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrBadSubstitution } // scan arg[1] { param, err := t.parseParam(rejectColonClose, scanIdent) if err != nil { return nil, err } // param.Value = t.scanner.string() node.Args = append(node.Args, param) } // expect delimiter or close t.scanner.accept = acceptColon t.scanner.mode = scanIdent | scanRbrack switch t.scanner.scan() { case tokenRbrack: return node, nil case tokenIdent: // no-op default: return nil, ErrBadSubstitution } // scan arg[2] { param, err := t.parseParam(acceptNotClosing, scanIdent) if err != nil { return nil, err } node.Args = append(node.Args, param) } return node, t.consumeRbrack() } // parses the ${param%word} string function // parses the ${param%%word} string function // parses the ${param#word} string function // parses the ${param##word} string function func (t *Tree) parseRemoveFunc(name string, accept acceptFunc) (Node, error) { node := new(FuncNode) node.Param = name t.scanner.accept = accept t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrBadSubstitution } // scan arg[1] { param, err := t.parseParam(acceptNotClosing, scanIdent) if err != nil { return nil, err } // param.Value = t.scanner.string() node.Args = append(node.Args, param) } return node, t.consumeRbrack() } // parses the ${param/pattern/string} string function // parses the ${param//pattern/string} string function // parses the ${param/#pattern/string} string function // parses the ${param/%pattern/string} string function func (t *Tree) parseReplaceFunc(name string) (Node, error) { node := new(FuncNode) node.Param = name t.scanner.accept = acceptReplaceFunc t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrBadSubstitution } // scan arg[1] { param, err := t.parseParam(acceptNotSlash, scanIdent|scanEscape) if err != nil { return nil, err } node.Args = append(node.Args, param) } // expect delimiter t.scanner.accept = acceptSlash t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: // no-op default: return nil, ErrBadSubstitution } // check for blank string switch t.scanner.peek() { case '}': return node, t.consumeRbrack() } // scan arg[2] { param, err := t.parseParam(acceptNotClosing, scanIdent|scanEscape) if err != nil { return nil, err } node.Args = append(node.Args, param) } return node, t.consumeRbrack() } // parses the ${parameter=word} string function // parses the ${parameter:=word} string function // parses the ${parameter:-word} string function // parses the ${parameter:?word} string function // parses the ${parameter:+word} string function func (t *Tree) parseDefaultFunc(name string) (Node, error) { node := new(FuncNode) node.Param = name t.scanner.accept = acceptDefaultFunc if t.scanner.peek() == '=' { t.scanner.accept = acceptOneEqual } t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrParseDefaultFunction } // loop through all possible runes in default param for { // this acts as the break condition. Peek to see if we reached the end switch t.scanner.peek() { case '}': return node, t.consumeRbrack() } param, err := t.parseParam(acceptNotClosing, scanIdent) if err != nil { return nil, err } node.Args = append(node.Args, param) } } // parses the ${param,} string function // parses the ${param,,} string function // parses the ${param^} string function // parses the ${param^^} string function func (t *Tree) parseCasingFunc(name string) (Node, error) { node := new(FuncNode) node.Param = name t.scanner.accept = acceptCasingFunc t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrBadSubstitution } return node, t.consumeRbrack() } // parses the ${#param} string function func (t *Tree) parseLenFunc() (Node, error) { node := new(FuncNode) t.scanner.accept = acceptOneHash t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Name = t.scanner.string() default: return nil, ErrBadSubstitution } t.scanner.accept = acceptIdent t.scanner.mode = scanIdent switch t.scanner.scan() { case tokenIdent: node.Param = t.scanner.string() default: return nil, ErrBadSubstitution } return node, t.consumeRbrack() } // consumeRbrack consumes a right closing bracket. If a closing // bracket token is not consumed an ErrBadSubstitution is returned. func (t *Tree) consumeRbrack() error { t.scanner.mode = scanRbrack if t.scanner.scan() != tokenRbrack { return ErrBadSubstitution } return nil } // consumeDelimiter consumes a function argument delimiter. If a // delimiter is not consumed an ErrBadSubstitution is returned. // func (t *Tree) consumeDelimiter(accept acceptFunc, mode uint) error { // t.scanner.accept = accept // t.scanner.mode = mode // if t.scanner.scan() != tokenRbrack { // return ErrBadSubstitution // } // return nil // }