lexer.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. package lexer
  2. import (
  3. "encoding/hex"
  4. "strings"
  5. "unicode"
  6. )
  7. // Lexer tokenizes SQL input.
  8. type Lexer struct {
  9. input string
  10. pos int // current position in input
  11. readPos int // reading position (after current char)
  12. ch byte // current char under examination
  13. line int // current line number (1-based)
  14. column int // current column number (1-based)
  15. }
  16. // New creates a new Lexer for the given input.
  17. func New(input string) *Lexer {
  18. l := &Lexer{
  19. input: input,
  20. line: 1,
  21. column: 0,
  22. }
  23. l.readChar()
  24. return l
  25. }
  26. // readChar advances the lexer by one character.
  27. func (l *Lexer) readChar() {
  28. if l.readPos >= len(l.input) {
  29. l.ch = 0
  30. } else {
  31. l.ch = l.input[l.readPos]
  32. }
  33. l.pos = l.readPos
  34. l.readPos++
  35. l.column++
  36. if l.ch == '\n' {
  37. l.line++
  38. l.column = 0
  39. }
  40. }
  41. // peekChar returns the next character without advancing.
  42. func (l *Lexer) peekChar() byte {
  43. if l.readPos >= len(l.input) {
  44. return 0
  45. }
  46. return l.input[l.readPos]
  47. }
  48. // NextToken returns the next token from the input.
  49. func (l *Lexer) NextToken() Token {
  50. l.skipWhitespace()
  51. tok := Token{
  52. Line: l.line,
  53. Column: l.column,
  54. }
  55. switch l.ch {
  56. case 0:
  57. tok.Type = TokenEOF
  58. tok.Literal = ""
  59. case '+':
  60. tok.Type = TokenPlus
  61. tok.Literal = "+"
  62. l.readChar()
  63. case '*':
  64. tok.Type = TokenStar
  65. tok.Literal = "*"
  66. l.readChar()
  67. case '/':
  68. tok.Type = TokenSlash
  69. tok.Literal = "/"
  70. l.readChar()
  71. case '%':
  72. tok.Type = TokenPercent
  73. tok.Literal = "%"
  74. l.readChar()
  75. case '&':
  76. tok.Type = TokenBitAnd
  77. tok.Literal = "&"
  78. l.readChar()
  79. case '~':
  80. tok.Type = TokenBitNot
  81. tok.Literal = "~"
  82. l.readChar()
  83. case '(':
  84. tok.Type = TokenLParen
  85. tok.Literal = "("
  86. l.readChar()
  87. case ')':
  88. tok.Type = TokenRParen
  89. tok.Literal = ")"
  90. l.readChar()
  91. case ',':
  92. tok.Type = TokenComma
  93. tok.Literal = ","
  94. l.readChar()
  95. case ';':
  96. tok.Type = TokenSemicolon
  97. tok.Literal = ";"
  98. l.readChar()
  99. case '.':
  100. tok.Type = TokenDot
  101. tok.Literal = "."
  102. l.readChar()
  103. case '=':
  104. tok.Type = TokenEq
  105. tok.Literal = "="
  106. l.readChar()
  107. case '<':
  108. if l.peekChar() == '=' {
  109. l.readChar()
  110. tok.Type = TokenLte
  111. tok.Literal = "<="
  112. } else if l.peekChar() == '>' {
  113. l.readChar()
  114. tok.Type = TokenNeq
  115. tok.Literal = "<>"
  116. } else if l.peekChar() == '<' {
  117. l.readChar()
  118. tok.Type = TokenShiftLeft
  119. tok.Literal = "<<"
  120. } else {
  121. tok.Type = TokenLt
  122. tok.Literal = "<"
  123. }
  124. l.readChar()
  125. case '>':
  126. if l.peekChar() == '=' {
  127. l.readChar()
  128. tok.Type = TokenGte
  129. tok.Literal = ">="
  130. } else if l.peekChar() == '>' {
  131. l.readChar()
  132. tok.Type = TokenShiftRight
  133. tok.Literal = ">>"
  134. } else {
  135. tok.Type = TokenGt
  136. tok.Literal = ">"
  137. }
  138. l.readChar()
  139. case '!':
  140. if l.peekChar() == '=' {
  141. l.readChar()
  142. tok.Type = TokenNeq
  143. tok.Literal = "!="
  144. l.readChar()
  145. } else {
  146. tok.Type = TokenError
  147. tok.Literal = "unexpected character: !"
  148. l.readChar()
  149. }
  150. case '|':
  151. if l.peekChar() == '|' {
  152. l.readChar()
  153. tok.Type = TokenConcat
  154. tok.Literal = "||"
  155. l.readChar()
  156. } else {
  157. tok.Type = TokenBitOr
  158. tok.Literal = "|"
  159. l.readChar()
  160. }
  161. case '-':
  162. if l.peekChar() == '-' {
  163. // Line comment
  164. tok = l.readLineComment()
  165. } else if l.peekChar() == '>' {
  166. l.readChar() // consume '-'
  167. if l.peekChar() == '>' {
  168. l.readChar() // consume the first '>'
  169. tok.Type = TokenArrowText
  170. tok.Literal = "->>"
  171. } else {
  172. tok.Type = TokenArrow
  173. tok.Literal = "->"
  174. }
  175. l.readChar()
  176. } else {
  177. tok.Type = TokenMinus
  178. tok.Literal = "-"
  179. l.readChar()
  180. }
  181. case '\'':
  182. tok = l.readString()
  183. case '"':
  184. tok = l.readQuotedIdentifier('"')
  185. case '`':
  186. tok = l.readQuotedIdentifier('`')
  187. case '[':
  188. tok = l.readBracketIdentifier()
  189. default:
  190. if (l.ch == 'x' || l.ch == 'X') && l.peekChar() == '\'' {
  191. tok = l.readBlob()
  192. } else if isLetter(l.ch) || l.ch == '_' {
  193. tok = l.readIdentifier()
  194. } else if isDigit(l.ch) {
  195. tok = l.readNumber()
  196. } else {
  197. tok.Type = TokenError
  198. tok.Literal = "unexpected character: " + string(l.ch)
  199. l.readChar()
  200. }
  201. }
  202. return tok
  203. }
  204. // skipWhitespace skips spaces, tabs, and newlines.
  205. func (l *Lexer) skipWhitespace() {
  206. for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' {
  207. l.readChar()
  208. }
  209. // Also skip block comments
  210. if l.ch == '/' && l.peekChar() == '*' {
  211. l.skipBlockComment()
  212. l.skipWhitespace()
  213. }
  214. }
  215. // skipBlockComment skips /* ... */ comments.
  216. func (l *Lexer) skipBlockComment() {
  217. l.readChar() // skip /
  218. l.readChar() // skip *
  219. for {
  220. if l.ch == 0 {
  221. return // EOF in comment
  222. }
  223. if l.ch == '*' && l.peekChar() == '/' {
  224. l.readChar() // skip *
  225. l.readChar() // skip /
  226. return
  227. }
  228. l.readChar()
  229. }
  230. }
  231. // readLineComment reads a -- line comment.
  232. func (l *Lexer) readLineComment() Token {
  233. tok := Token{
  234. Type: TokenComment,
  235. Line: l.line,
  236. Column: l.column,
  237. }
  238. startPos := l.pos
  239. for l.ch != '\n' && l.ch != 0 {
  240. l.readChar()
  241. }
  242. tok.Literal = l.input[startPos:l.pos]
  243. return tok
  244. }
  245. // readString reads a 'string literal'.
  246. func (l *Lexer) readString() Token {
  247. tok := Token{
  248. Type: TokenString,
  249. Line: l.line,
  250. Column: l.column,
  251. }
  252. l.readChar() // skip opening quote
  253. var sb strings.Builder
  254. for {
  255. if l.ch == 0 {
  256. tok.Type = TokenError
  257. tok.Literal = "unterminated string"
  258. return tok
  259. }
  260. if l.ch == '\'' {
  261. if l.peekChar() == '\'' {
  262. // Escaped quote
  263. sb.WriteByte('\'')
  264. l.readChar()
  265. l.readChar()
  266. } else {
  267. // End of string
  268. l.readChar()
  269. break
  270. }
  271. } else {
  272. sb.WriteByte(l.ch)
  273. l.readChar()
  274. }
  275. }
  276. tok.Literal = sb.String()
  277. return tok
  278. }
  279. // readQuotedIdentifier reads a "quoted identifier" or `backtick identifier`.
  280. func (l *Lexer) readQuotedIdentifier(quote byte) Token {
  281. tok := Token{
  282. Type: TokenIdent,
  283. Line: l.line,
  284. Column: l.column,
  285. }
  286. l.readChar() // skip opening quote
  287. startPos := l.pos
  288. for l.ch != quote && l.ch != 0 {
  289. l.readChar()
  290. }
  291. if l.ch == 0 {
  292. tok.Type = TokenError
  293. tok.Literal = "unterminated identifier"
  294. return tok
  295. }
  296. tok.Literal = l.input[startPos:l.pos]
  297. l.readChar() // skip closing quote
  298. return tok
  299. }
  300. // readBracketIdentifier reads a [bracket identifier] (SQL Server style).
  301. func (l *Lexer) readBracketIdentifier() Token {
  302. tok := Token{
  303. Type: TokenIdent,
  304. Line: l.line,
  305. Column: l.column,
  306. }
  307. l.readChar() // skip [
  308. startPos := l.pos
  309. for l.ch != ']' && l.ch != 0 {
  310. l.readChar()
  311. }
  312. if l.ch == 0 {
  313. tok.Type = TokenError
  314. tok.Literal = "unterminated identifier"
  315. return tok
  316. }
  317. tok.Literal = l.input[startPos:l.pos]
  318. l.readChar() // skip ]
  319. return tok
  320. }
  321. // readBlob reads a X'hex' blob literal. Whitespace between hex digits is
  322. // ignored (SQLite allows it). The token literal holds the decoded raw bytes so
  323. // consumers never have to re-parse the hex form.
  324. func (l *Lexer) readBlob() Token {
  325. tok := Token{
  326. Type: TokenBlob,
  327. Line: l.line,
  328. Column: l.column,
  329. }
  330. l.readChar() // skip x/X
  331. l.readChar() // skip opening quote
  332. var sb strings.Builder
  333. for l.ch != '\'' && l.ch != 0 {
  334. if isHexDigit(l.ch) {
  335. sb.WriteByte(l.ch)
  336. } else if l.ch != ' ' && l.ch != '\t' && l.ch != '\n' && l.ch != '\r' {
  337. tok.Type = TokenError
  338. tok.Literal = "invalid character in blob literal: " + string(l.ch)
  339. return tok
  340. }
  341. l.readChar()
  342. }
  343. if l.ch == 0 {
  344. tok.Type = TokenError
  345. tok.Literal = "unterminated blob literal"
  346. return tok
  347. }
  348. l.readChar() // skip closing quote
  349. if sb.Len()%2 != 0 {
  350. tok.Type = TokenError
  351. tok.Literal = "blob literal must contain an even number of hex digits"
  352. return tok
  353. }
  354. decoded, err := hex.DecodeString(sb.String())
  355. if err != nil {
  356. tok.Type = TokenError
  357. tok.Literal = "invalid blob literal: " + err.Error()
  358. return tok
  359. }
  360. tok.Literal = string(decoded)
  361. return tok
  362. }
  363. // readIdentifier reads an identifier or keyword.
  364. func (l *Lexer) readIdentifier() Token {
  365. tok := Token{
  366. Line: l.line,
  367. Column: l.column,
  368. }
  369. startPos := l.pos
  370. for isLetter(l.ch) || isDigit(l.ch) || l.ch == '_' {
  371. l.readChar()
  372. }
  373. literal := l.input[startPos:l.pos]
  374. tok.Literal = literal
  375. tok.Type = LookupKeyword(strings.ToUpper(literal))
  376. return tok
  377. }
  378. // readNumber reads an integer or float.
  379. func (l *Lexer) readNumber() Token {
  380. tok := Token{
  381. Type: TokenNumber,
  382. Line: l.line,
  383. Column: l.column,
  384. }
  385. startPos := l.pos
  386. // Read integer part
  387. for isDigit(l.ch) {
  388. l.readChar()
  389. }
  390. // Check for decimal point
  391. if l.ch == '.' && isDigit(l.peekChar()) {
  392. l.readChar() // skip .
  393. for isDigit(l.ch) {
  394. l.readChar()
  395. }
  396. }
  397. // Check for exponent
  398. if l.ch == 'e' || l.ch == 'E' {
  399. l.readChar()
  400. if l.ch == '+' || l.ch == '-' {
  401. l.readChar()
  402. }
  403. for isDigit(l.ch) {
  404. l.readChar()
  405. }
  406. }
  407. tok.Literal = l.input[startPos:l.pos]
  408. return tok
  409. }
  410. // Tokenize returns all tokens from the input.
  411. func (l *Lexer) Tokenize() []Token {
  412. var tokens []Token
  413. for {
  414. tok := l.NextToken()
  415. tokens = append(tokens, tok)
  416. if tok.Type == TokenEOF || tok.Type == TokenError {
  417. break
  418. }
  419. }
  420. return tokens
  421. }
  422. func isLetter(ch byte) bool {
  423. return unicode.IsLetter(rune(ch))
  424. }
  425. func isDigit(ch byte) bool {
  426. return ch >= '0' && ch <= '9'
  427. }
  428. func isHexDigit(ch byte) bool {
  429. return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
  430. }