2
0

lexer_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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. name: "single pipe",
  284. input: "|",
  285. errMsg: "unexpected character: |",
  286. },
  287. }
  288. for _, tt := range tests {
  289. t.Run(tt.name, func(t *testing.T) {
  290. l := New(tt.input)
  291. tok := l.NextToken()
  292. if tok.Type != TokenError {
  293. t.Errorf("expected TokenError, got %v", tok.Type)
  294. }
  295. if tok.Literal != tt.errMsg {
  296. t.Errorf("expected error %q, got %q", tt.errMsg, tok.Literal)
  297. }
  298. })
  299. }
  300. }
  301. func TestLexerComplexExpressions(t *testing.T) {
  302. input := "WHERE a = 1 AND b > 2 OR c <= 3 AND NOT d <> 4"
  303. expected := []struct {
  304. typ TokenType
  305. literal string
  306. }{
  307. {TokenWHERE, "WHERE"},
  308. {TokenIdent, "a"},
  309. {TokenEq, "="},
  310. {TokenNumber, "1"},
  311. {TokenAND, "AND"},
  312. {TokenIdent, "b"},
  313. {TokenGt, ">"},
  314. {TokenNumber, "2"},
  315. {TokenOR, "OR"},
  316. {TokenIdent, "c"},
  317. {TokenLte, "<="},
  318. {TokenNumber, "3"},
  319. {TokenAND, "AND"},
  320. {TokenNOT, "NOT"},
  321. {TokenIdent, "d"},
  322. {TokenNeq, "<>"},
  323. {TokenNumber, "4"},
  324. {TokenEOF, ""},
  325. }
  326. l := New(input)
  327. for i, exp := range expected {
  328. tok := l.NextToken()
  329. if tok.Type != exp.typ {
  330. t.Errorf("token[%d]: expected type %v, got %v", i, exp.typ, tok.Type)
  331. }
  332. if tok.Literal != exp.literal {
  333. t.Errorf("token[%d]: expected literal %q, got %q", i, exp.literal, tok.Literal)
  334. }
  335. }
  336. }
  337. func TestLexerJoinKeywords(t *testing.T) {
  338. input := "LEFT OUTER JOIN t ON a.id = b.id"
  339. expected := []TokenType{
  340. TokenLEFT, TokenOUTER, TokenJOIN, TokenIdent,
  341. TokenON, TokenIdent, TokenDot, TokenIdent,
  342. TokenEq, TokenIdent, TokenDot, TokenIdent,
  343. TokenEOF,
  344. }
  345. l := New(input)
  346. tokens := l.Tokenize()
  347. if len(tokens) != len(expected) {
  348. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  349. return
  350. }
  351. for i, exp := range expected {
  352. if tokens[i].Type != exp {
  353. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  354. }
  355. }
  356. }
  357. func TestLexerCaseExpression(t *testing.T) {
  358. input := "CASE WHEN x = 1 THEN 'one' ELSE 'other' END"
  359. expected := []TokenType{
  360. TokenCASE, TokenWHEN, TokenIdent, TokenEq, TokenNumber,
  361. TokenTHEN, TokenString, TokenELSE, TokenString, TokenEND,
  362. TokenEOF,
  363. }
  364. l := New(input)
  365. tokens := l.Tokenize()
  366. if len(tokens) != len(expected) {
  367. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  368. return
  369. }
  370. for i, exp := range expected {
  371. if tokens[i].Type != exp {
  372. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  373. }
  374. }
  375. }
  376. func TestLexerSubquery(t *testing.T) {
  377. input := "WHERE id IN (SELECT user_id FROM orders)"
  378. expected := []TokenType{
  379. TokenWHERE, TokenIdent, TokenIN, TokenLParen,
  380. TokenSELECT, TokenIdent, TokenFROM, TokenIdent,
  381. TokenRParen, TokenEOF,
  382. }
  383. l := New(input)
  384. tokens := l.Tokenize()
  385. if len(tokens) != len(expected) {
  386. t.Errorf("expected %d tokens, got %d", len(expected), len(tokens))
  387. return
  388. }
  389. for i, exp := range expected {
  390. if tokens[i].Type != exp {
  391. t.Errorf("token[%d]: expected %v, got %v", i, exp, tokens[i].Type)
  392. }
  393. }
  394. }
  395. func BenchmarkLexer(b *testing.B) {
  396. input := `
  397. SELECT u.id, u.name, u.email, COUNT(o.id) as order_count
  398. FROM users u
  399. LEFT JOIN orders o ON u.id = o.user_id
  400. WHERE u.active = TRUE AND u.created_at >= '2024-01-01'
  401. GROUP BY u.id, u.name, u.email
  402. HAVING COUNT(o.id) > 5
  403. ORDER BY order_count DESC
  404. LIMIT 100 OFFSET 0;
  405. `
  406. b.ResetTimer()
  407. for i := 0; i < b.N; i++ {
  408. l := New(input)
  409. for {
  410. tok := l.NextToken()
  411. if tok.Type == TokenEOF {
  412. break
  413. }
  414. }
  415. }
  416. }