2
0

lexer_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. package lexer
  2. import (
  3. "testing"
  4. )
  5. func TestLexerSingleTokens(t *testing.T) {
  6. tests := []struct {
  7. input string
  8. expected TokenType
  9. literal string
  10. }{
  11. // Operators
  12. {"+", TokenPlus, "+"},
  13. {"-", TokenMinus, "-"},
  14. {"*", TokenStar, "*"},
  15. {"/", TokenSlash, "/"},
  16. {"%", TokenPercent, "%"},
  17. {"||", TokenConcat, "||"},
  18. {"=", TokenEq, "="},
  19. {"<>", TokenNeq, "<>"},
  20. {"!=", TokenNeq, "!="},
  21. {"<", TokenLt, "<"},
  22. {"<=", TokenLte, "<="},
  23. {">", TokenGt, ">"},
  24. {">=", TokenGte, ">="},
  25. // Punctuation
  26. {"(", TokenLParen, "("},
  27. {")", TokenRParen, ")"},
  28. {",", TokenComma, ","},
  29. {";", TokenSemicolon, ";"},
  30. {".", TokenDot, "."},
  31. // Keywords (case insensitive)
  32. {"SELECT", TokenSELECT, "SELECT"},
  33. {"select", TokenSELECT, "select"},
  34. {"SeLeCt", TokenSELECT, "SeLeCt"},
  35. {"FROM", TokenFROM, "FROM"},
  36. {"WHERE", TokenWHERE, "WHERE"},
  37. {"AND", TokenAND, "AND"},
  38. {"OR", TokenOR, "OR"},
  39. {"NOT", TokenNOT, "NOT"},
  40. {"INSERT", TokenINSERT, "INSERT"},
  41. {"INTO", TokenINTO, "INTO"},
  42. {"VALUES", TokenVALUES, "VALUES"},
  43. {"UPDATE", TokenUPDATE, "UPDATE"},
  44. {"SET", TokenSET, "SET"},
  45. {"DELETE", TokenDELETE, "DELETE"},
  46. {"CREATE", TokenCREATE, "CREATE"},
  47. {"DROP", TokenDROP, "DROP"},
  48. {"TABLE", TokenTABLE, "TABLE"},
  49. {"PRIMARY", TokenPRIMARY, "PRIMARY"},
  50. {"KEY", TokenKEY, "KEY"},
  51. {"NULL", TokenNULL, "NULL"},
  52. {"TRUE", TokenTRUE, "TRUE"},
  53. {"FALSE", TokenFALSE, "FALSE"},
  54. {"INTEGER", TokenINTEGER, "INTEGER"},
  55. {"TEXT", TokenTEXT, "TEXT"},
  56. {"BLOB", TokenBLOB, "BLOB"},
  57. {"REAL", TokenREAL, "REAL"},
  58. // Identifiers
  59. {"foo", TokenIdent, "foo"},
  60. {"_bar", TokenIdent, "_bar"},
  61. {"table123", TokenIdent, "table123"},
  62. {"CamelCase", TokenIdent, "CamelCase"},
  63. // Numbers
  64. {"42", TokenNumber, "42"},
  65. {"3.14", TokenNumber, "3.14"},
  66. {"1e10", TokenNumber, "1e10"},
  67. {"2.5e-3", TokenNumber, "2.5e-3"},
  68. {"0", TokenNumber, "0"},
  69. // Strings
  70. {"'hello'", TokenString, "hello"},
  71. {"'it''s escaped'", TokenString, "it's escaped"},
  72. {"''", TokenString, ""},
  73. }
  74. for _, tt := range tests {
  75. t.Run(tt.input, func(t *testing.T) {
  76. l := New(tt.input)
  77. tok := l.NextToken()
  78. if tok.Type != tt.expected {
  79. t.Errorf("expected type %v, got %v", tt.expected, tok.Type)
  80. }
  81. if tok.Literal != tt.literal {
  82. t.Errorf("expected literal %q, got %q", tt.literal, tok.Literal)
  83. }
  84. })
  85. }
  86. }
  87. func TestLexerQuotedIdentifiers(t *testing.T) {
  88. tests := []struct {
  89. input string
  90. literal string
  91. }{
  92. {`"column name"`, "column name"},
  93. {"`backtick`", "backtick"},
  94. {"[bracket]", "bracket"},
  95. {`"with spaces"`, "with spaces"},
  96. }
  97. for _, tt := range tests {
  98. t.Run(tt.input, func(t *testing.T) {
  99. l := New(tt.input)
  100. tok := l.NextToken()
  101. if tok.Type != TokenIdent {
  102. t.Errorf("expected TokenIdent, got %v", tok.Type)
  103. }
  104. if tok.Literal != tt.literal {
  105. t.Errorf("expected literal %q, got %q", tt.literal, tok.Literal)
  106. }
  107. })
  108. }
  109. }
  110. func TestLexerSelectStatement(t *testing.T) {
  111. input := "SELECT * FROM users WHERE id = 1;"
  112. expected := []struct {
  113. typ TokenType
  114. literal string
  115. }{
  116. {TokenSELECT, "SELECT"},
  117. {TokenStar, "*"},
  118. {TokenFROM, "FROM"},
  119. {TokenIdent, "users"},
  120. {TokenWHERE, "WHERE"},
  121. {TokenIdent, "id"},
  122. {TokenEq, "="},
  123. {TokenNumber, "1"},
  124. {TokenSemicolon, ";"},
  125. {TokenEOF, ""},
  126. }
  127. l := New(input)
  128. for i, exp := range expected {
  129. tok := l.NextToken()
  130. if tok.Type != exp.typ {
  131. t.Errorf("token[%d]: expected type %v, got %v", i, exp.typ, tok.Type)
  132. }
  133. if tok.Literal != exp.literal {
  134. t.Errorf("token[%d]: expected literal %q, got %q", i, exp.literal, tok.Literal)
  135. }
  136. }
  137. }
  138. func TestLexerInsertStatement(t *testing.T) {
  139. input := "INSERT INTO users (name, age) VALUES ('John', 30);"
  140. expected := []struct {
  141. typ TokenType
  142. literal string
  143. }{
  144. {TokenINSERT, "INSERT"},
  145. {TokenINTO, "INTO"},
  146. {TokenIdent, "users"},
  147. {TokenLParen, "("},
  148. {TokenIdent, "name"},
  149. {TokenComma, ","},
  150. {TokenIdent, "age"},
  151. {TokenRParen, ")"},
  152. {TokenVALUES, "VALUES"},
  153. {TokenLParen, "("},
  154. {TokenString, "John"},
  155. {TokenComma, ","},
  156. {TokenNumber, "30"},
  157. {TokenRParen, ")"},
  158. {TokenSemicolon, ";"},
  159. {TokenEOF, ""},
  160. }
  161. l := New(input)
  162. for i, exp := range expected {
  163. tok := l.NextToken()
  164. if tok.Type != exp.typ {
  165. t.Errorf("token[%d]: expected type %v, got %v", i, exp.typ, tok.Type)
  166. }
  167. if tok.Literal != exp.literal {
  168. t.Errorf("token[%d]: expected literal %q, got %q", i, exp.literal, tok.Literal)
  169. }
  170. }
  171. }
  172. func TestLexerCreateTable(t *testing.T) {
  173. input := `CREATE TABLE users (
  174. id INTEGER PRIMARY KEY,
  175. name TEXT NOT NULL,
  176. email VARCHAR(255) UNIQUE
  177. );`
  178. l := New(input)
  179. tokens := l.Tokenize()
  180. // Just verify we got all expected token types
  181. expectedTypes := []TokenType{
  182. TokenCREATE, TokenTABLE, TokenIdent, TokenLParen,
  183. TokenIdent, TokenINTEGER, TokenPRIMARY, TokenKEY, TokenComma,
  184. TokenIdent, TokenTEXT, TokenNOT, TokenNULL, TokenComma,
  185. TokenIdent, TokenVARCHAR, TokenLParen, TokenNumber, TokenRParen, TokenUNIQUE,
  186. TokenRParen, TokenSemicolon, TokenEOF,
  187. }
  188. if len(tokens) != len(expectedTypes) {
  189. t.Errorf("expected %d tokens, got %d", len(expectedTypes), len(tokens))
  190. for i, tok := range tokens {
  191. t.Logf("token[%d]: %v", i, tok)
  192. }
  193. return
  194. }
  195. for i, exp := range expectedTypes {
  196. if tokens[i].Type != exp {
  197. t.Errorf("token[%d]: expected %v, got %v (%q)", i, exp, tokens[i].Type, tokens[i].Literal)
  198. }
  199. }
  200. }
  201. func TestLexerComments(t *testing.T) {
  202. tests := []struct {
  203. name string
  204. input string
  205. expected []TokenType
  206. }{
  207. {
  208. name: "line comment",
  209. input: "SELECT -- comment\n* FROM t",
  210. expected: []TokenType{TokenSELECT, TokenComment, TokenStar, TokenFROM, TokenIdent, TokenEOF},
  211. },
  212. {
  213. name: "block comment",
  214. input: "SELECT /* comment */ * FROM t",
  215. expected: []TokenType{TokenSELECT, TokenStar, TokenFROM, TokenIdent, TokenEOF},
  216. },
  217. {
  218. name: "multiline block comment",
  219. input: "SELECT /* line1\nline2 */ * FROM t",
  220. expected: []TokenType{TokenSELECT, TokenStar, TokenFROM, TokenIdent, TokenEOF},
  221. },
  222. }
  223. for _, tt := range tests {
  224. t.Run(tt.name, func(t *testing.T) {
  225. l := New(tt.input)
  226. tokens := l.Tokenize()
  227. if len(tokens) != len(tt.expected) {
  228. t.Errorf("expected %d tokens, got %d", len(tt.expected), len(tokens))
  229. for i, tok := range tokens {
  230. t.Logf("token[%d]: %v", i, tok)
  231. }
  232. return
  233. }
  234. for i, exp := range tt.expected {
  235. if tokens[i].Type != exp {
  236. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  237. }
  238. }
  239. })
  240. }
  241. }
  242. func TestLexerLineTracking(t *testing.T) {
  243. input := "SELECT\n*\nFROM t"
  244. l := New(input)
  245. // SELECT on line 1
  246. tok := l.NextToken()
  247. if tok.Line != 1 {
  248. t.Errorf("SELECT: expected line 1, got %d", tok.Line)
  249. }
  250. // * on line 2
  251. tok = l.NextToken()
  252. if tok.Line != 2 {
  253. t.Errorf("*: expected line 2, got %d", tok.Line)
  254. }
  255. // FROM on line 3
  256. tok = l.NextToken()
  257. if tok.Line != 3 {
  258. t.Errorf("FROM: expected line 3, got %d", tok.Line)
  259. }
  260. }
  261. func TestLexerErrors(t *testing.T) {
  262. tests := []struct {
  263. name string
  264. input string
  265. errMsg string
  266. }{
  267. {
  268. name: "unterminated string",
  269. input: "'hello",
  270. errMsg: "unterminated string",
  271. },
  272. {
  273. name: "unterminated quoted identifier",
  274. input: `"hello`,
  275. errMsg: "unterminated identifier",
  276. },
  277. {
  278. name: "unexpected character",
  279. input: "@",
  280. errMsg: "unexpected character: @",
  281. },
  282. }
  283. for _, tt := range tests {
  284. t.Run(tt.name, func(t *testing.T) {
  285. l := New(tt.input)
  286. tok := l.NextToken()
  287. if tok.Type != TokenError {
  288. t.Errorf("expected TokenError, got %v", tok.Type)
  289. }
  290. if tok.Literal != tt.errMsg {
  291. t.Errorf("expected error %q, got %q", tt.errMsg, tok.Literal)
  292. }
  293. })
  294. }
  295. }
  296. func TestLexerComplexExpressions(t *testing.T) {
  297. input := "WHERE a = 1 AND b > 2 OR c <= 3 AND NOT d <> 4"
  298. expected := []struct {
  299. typ TokenType
  300. literal string
  301. }{
  302. {TokenWHERE, "WHERE"},
  303. {TokenIdent, "a"},
  304. {TokenEq, "="},
  305. {TokenNumber, "1"},
  306. {TokenAND, "AND"},
  307. {TokenIdent, "b"},
  308. {TokenGt, ">"},
  309. {TokenNumber, "2"},
  310. {TokenOR, "OR"},
  311. {TokenIdent, "c"},
  312. {TokenLte, "<="},
  313. {TokenNumber, "3"},
  314. {TokenAND, "AND"},
  315. {TokenNOT, "NOT"},
  316. {TokenIdent, "d"},
  317. {TokenNeq, "<>"},
  318. {TokenNumber, "4"},
  319. {TokenEOF, ""},
  320. }
  321. l := New(input)
  322. for i, exp := range expected {
  323. tok := l.NextToken()
  324. if tok.Type != exp.typ {
  325. t.Errorf("token[%d]: expected type %v, got %v", i, exp.typ, tok.Type)
  326. }
  327. if tok.Literal != exp.literal {
  328. t.Errorf("token[%d]: expected literal %q, got %q", i, exp.literal, tok.Literal)
  329. }
  330. }
  331. }
  332. func TestLexerJoinKeywords(t *testing.T) {
  333. input := "LEFT OUTER JOIN t ON a.id = b.id"
  334. expected := []TokenType{
  335. TokenLEFT, TokenOUTER, TokenJOIN, TokenIdent,
  336. TokenON, TokenIdent, TokenDot, TokenIdent,
  337. TokenEq, TokenIdent, TokenDot, TokenIdent,
  338. TokenEOF,
  339. }
  340. l := New(input)
  341. tokens := l.Tokenize()
  342. if len(tokens) != len(expected) {
  343. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  344. return
  345. }
  346. for i, exp := range expected {
  347. if tokens[i].Type != exp {
  348. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  349. }
  350. }
  351. }
  352. func TestLexerCaseExpression(t *testing.T) {
  353. input := "CASE WHEN x = 1 THEN 'one' ELSE 'other' END"
  354. expected := []TokenType{
  355. TokenCASE, TokenWHEN, TokenIdent, TokenEq, TokenNumber,
  356. TokenTHEN, TokenString, TokenELSE, TokenString, TokenEND,
  357. TokenEOF,
  358. }
  359. l := New(input)
  360. tokens := l.Tokenize()
  361. if len(tokens) != len(expected) {
  362. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  363. return
  364. }
  365. for i, exp := range expected {
  366. if tokens[i].Type != exp {
  367. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  368. }
  369. }
  370. }
  371. func TestLexerSubquery(t *testing.T) {
  372. input := "WHERE id IN (SELECT user_id FROM orders)"
  373. expected := []TokenType{
  374. TokenWHERE, TokenIdent, TokenIN, TokenLParen,
  375. TokenSELECT, TokenIdent, TokenFROM, TokenIdent,
  376. TokenRParen, TokenEOF,
  377. }
  378. l := New(input)
  379. tokens := l.Tokenize()
  380. if len(tokens) != len(expected) {
  381. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  382. return
  383. }
  384. for i, exp := range expected {
  385. if tokens[i].Type != exp {
  386. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  387. }
  388. }
  389. }
  390. func TestLexerBitwiseOperators(t *testing.T) {
  391. input := "a & b | c << 2 >> 1 ~d"
  392. expected := []TokenType{
  393. TokenIdent, TokenBitAnd, TokenIdent, TokenBitOr, TokenIdent,
  394. TokenShiftLeft, TokenNumber, TokenShiftRight, TokenNumber,
  395. TokenBitNot, TokenIdent, TokenEOF,
  396. }
  397. tokens := New(input).Tokenize()
  398. if len(tokens) != len(expected) {
  399. t.Fatalf("expected %d tokens, got %d: %v", len(expected), len(tokens), tokens)
  400. }
  401. for i, exp := range expected {
  402. if tokens[i].Type != exp {
  403. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  404. }
  405. }
  406. }
  407. func TestLexerBlobLiteral(t *testing.T) {
  408. tokens := New("X'53514C697465'").Tokenize()
  409. if len(tokens) != 2 {
  410. t.Fatalf("expected 2 tokens, got %d: %v", len(tokens), tokens)
  411. }
  412. if tokens[0].Type != TokenBlob {
  413. t.Fatalf("expected TokenBlob, got %v", tokens[0].Type)
  414. }
  415. if tokens[0].Literal != "SQLite" {
  416. t.Fatalf("blob literal = %q, want %q", tokens[0].Literal, "SQLite")
  417. }
  418. }
  419. func TestLexerBlobLiteralWhitespaceAndErrors(t *testing.T) {
  420. tokens := New("x'53 51'").Tokenize()
  421. if tokens[0].Type != TokenBlob || tokens[0].Literal != "SQ" {
  422. t.Fatalf("blob with whitespace = %v %q", tokens[0].Type, tokens[0].Literal)
  423. }
  424. for _, input := range []string{"X'5'", "X'zz'", "X'5"} {
  425. tok := New(input).NextToken()
  426. if tok.Type != TokenError {
  427. t.Errorf("input %q: expected TokenError, got %v", input, tok.Type)
  428. }
  429. }
  430. }
  431. func BenchmarkLexer(b *testing.B) {
  432. input := `
  433. SELECT u.id, u.name, u.email, COUNT(o.id) as order_count
  434. FROM users u
  435. LEFT JOIN orders o ON u.id = o.user_id
  436. WHERE u.active = TRUE AND u.created_at >= '2024-01-01'
  437. GROUP BY u.id, u.name, u.email
  438. HAVING COUNT(o.id) > 5
  439. ORDER BY order_count DESC
  440. LIMIT 100 OFFSET 0;
  441. `
  442. b.ResetTimer()
  443. for i := 0; i < b.N; i++ {
  444. l := New(input)
  445. for {
  446. tok := l.NextToken()
  447. if tok.Type == TokenEOF {
  448. break
  449. }
  450. }
  451. }
  452. }