Files
db_in_45_steps_go/0303/sql_parser.go
T
2026-07-31 20:30:42 +02:00

162 lines
3.1 KiB
Go

package db0303
import (
"errors"
"strconv"
"strings"
)
type Parser struct {
buf string
pos int
}
func NewParser(s string) Parser {
return Parser{buf: s, pos: 0}
}
type StmtSelect struct {
table string
cols []string
keys []NamedCell
}
type NamedCell struct {
column string
value Cell
}
func isSpace(ch byte) bool {
switch ch {
case '\t', '\n', '\v', '\f', '\r', ' ':
return true
}
return false
}
func isAlpha(ch byte) bool {
return 'a' <= (ch|32) && (ch|32) <= 'z'
}
func isDigit(ch byte) bool {
return '0' <= ch && ch <= '9'
}
func isNameStart(ch byte) bool {
return isAlpha(ch) || ch == '_'
}
func isNameContinue(ch byte) bool {
return isAlpha(ch) || isDigit(ch) || ch == '_'
}
func isSeparator(ch byte) bool {
return ch < 128 && !isNameContinue(ch)
}
func (p *Parser) skipSpaces() {
for p.pos < len(p.buf) && isSpace(p.buf[p.pos]) {
p.pos += 1
}
}
func (p *Parser) tryKeyword(kw string) bool {
p.skipSpaces()
if !(p.pos+len(kw) <= len(p.buf) && strings.EqualFold(p.buf[p.pos:p.pos+len(kw)], kw)) {
return false
}
if p.pos+len(kw) < len(p.buf) && !isSeparator(p.buf[p.pos+len(kw)]) {
return false
}
p.pos += len(kw)
return true
}
func (p *Parser) tryPunctuation(tok string) bool {
p.skipSpaces()
if !(p.pos+len(tok) <= len(p.buf) && p.buf[p.pos:p.pos+len(tok)] == tok) {
return false
}
p.pos += len(tok)
return true
}
func (p *Parser) tryName() (string, bool) {
p.skipSpaces()
start, cur := p.pos, p.pos
if !(cur < len(p.buf) && isNameStart(p.buf[cur])) {
return "", false
}
cur++
for cur < len(p.buf) && isNameContinue(p.buf[cur]) {
cur++
}
p.pos = cur
return p.buf[start:cur], true
}
func (p *Parser) parseValue(out *Cell) error {
p.skipSpaces()
if p.pos >= len(p.buf) {
return errors.New("expect value")
}
ch := p.buf[p.pos]
if ch == '"' || ch == '\'' {
return p.parseString(out)
} else if isDigit(ch) || ch == '-' || ch == '+' {
return p.parseInt(out)
} else {
return errors.New("expect value")
}
}
func (p *Parser) parseString(out *Cell) error {
quote := p.buf[p.pos]
cur := p.pos + 1
for cur < len(p.buf) {
ch := p.buf[cur]
if ch == '\\' {
cur++
if cur < len(p.buf) && (p.buf[cur] == '"' || p.buf[cur] == '\'') {
out.Str = append(out.Str, p.buf[cur])
cur++
} else {
return errors.New("bad escape")
}
} else if ch == quote {
out.Type = TypeStr
p.pos = cur + 1
return nil
} else {
out.Str = append(out.Str, p.buf[cur])
cur++
}
}
return errors.New("string is not terminated")
}
func (p *Parser) parseInt(out *Cell) (err error) {
start, cur := p.pos, p.pos
if p.buf[cur] == '-' || p.buf[cur] == '+' {
cur++
}
for cur < len(p.buf) && isDigit(p.buf[cur]) {
cur++
}
if out.I64, err = strconv.ParseInt(p.buf[start:cur], 10, 64); err != nil {
return err
}
out.Type = TypeI64
p.pos = cur
return nil
}
func (p *Parser) parseEqual(out *NamedCell) error
func (p *Parser) parseSelect(out *StmtSelect) error
func (p *Parser) parseWhere(out *[]NamedCell) error
func (p *Parser) isEnd() bool {
p.skipSpaces()
return p.pos >= len(p.buf)
}
// QzBQWVJJOUhU https://trialofcode.org/