Files
five/_FiveSql2/test/bench_parser.prg
Charles KWON OhJun 486e466592 feat: FiveSql2 43/43, @byref, mutable closure, RTL 479, DateTime fix
Major changes since last commit:
- FiveSql2 SQL:1999 engine (10,458 LOC) — 43/43 ALL PASS
- 21 compiler/runtime bugs fixed (short-circuit AND/OR, FOR LOOP, etc.)
- @byref pass-by-reference via RefCell pattern
- Mutable closure capture (EnsureLocalRef + RefCell sharing)
- RTL: 400 → 479 functions (+79: file, string, datetime, hash, UTF-8)
- DateTime/Timestamp fully working (hb_DateTime, hb_Hour/Min/Sec, display)
- Reserved word guard (39 keywords blocked from function calls)
- AEval arg order fix (element before index)
- Closure capture redecl fix (unique _cap_ names per block)
- Hash/string indexing in ArrayPush/ArrayPop
- Harbour compat test suite: 51/51
- 4 docs: Porting Report, Implementation Plan, Optimization Plan, Commercialization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:35:37 +09:00

83 lines
2.9 KiB
Plaintext

/*
* bench_parser.prg — Parser performance benchmark
* Compares TSqlParser (original) vs TSqlParser2 (Pratt)
*
* Copyright (c) 2025-2026 Charles KWON (Charles KWON OhJun)
* All rights reserved.
*/
#include "FiveSqlDef.ch"
#include "hbclass.ch"
#define ITERS 10000
PROCEDURE Main()
LOCAL aSQL, i, j, t0, t1, oLex, aTokens, oP, h
LOCAL nT1, nT2, aAllTokens
aSQL := { ;
"SELECT name, salary FROM employees WHERE salary > 5000", ;
"SELECT e.name, o.product FROM employees e JOIN orders o ON e.id = o.emp_id WHERE o.amount > 100", ;
"SELECT dept, COUNT(*) AS cnt, AVG(salary) AS avg_sal FROM employees GROUP BY dept HAVING AVG(salary) > 5000 ORDER BY avg_sal DESC", ;
"SELECT name FROM employees WHERE id IN (SELECT emp_id FROM orders WHERE amount > 500)", ;
"SELECT name, salary, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees", ;
"SELECT name, CASE WHEN salary > 7000 THEN 'High' WHEN salary > 5000 THEN 'Mid' ELSE 'Low' END AS tier FROM employees WHERE dept = 'Engineering' OR (salary > 6000 AND mgr_id IS NOT NULL)", ;
"INSERT INTO employees (id, name, dept, salary) VALUES (99, 'Test', 'QA', 5000)", ;
"UPDATE employees SET salary = salary * 1.1 + 500 WHERE dept = 'Sales' AND salary BETWEEN 4000 AND 6000" ;
}
? "================================================================"
? " Parser Benchmark: TSqlParser vs TSqlParser2 (Pratt)"
? " Queries: " + hb_ntos( Len( aSQL ) ) + " Iterations: " + hb_ntos( ITERS )
? "================================================================"
?
/* Pre-tokenize all queries */
aAllTokens := {}
FOR i := 1 TO Len( aSQL )
oLex := TSqlLexer():New( aSQL[ i ] )
oLex:Tokenize()
AAdd( aAllTokens, oLex:GetTokens() )
NEXT
/* Benchmark TSqlParser (original) */
t0 := hb_MilliSeconds()
FOR j := 1 TO ITERS
FOR i := 1 TO Len( aSQL )
oP := TSqlParser():New( aAllTokens[ i ], {} )
h := oP:Parse()
NEXT
NEXT
t1 := hb_MilliSeconds()
nT1 := t1 - t0
? " TSqlParser (original) : " + Str( nT1, 8 ) + " ms (" + ;
Str( nT1 * 1000 / ( ITERS * Len( aSQL ) ), 6, 1 ) + " us/parse)"
/* Benchmark TSqlParser2 (Pratt) */
t0 := hb_MilliSeconds()
FOR j := 1 TO ITERS
FOR i := 1 TO Len( aSQL )
oP := TSqlParser2():New( aAllTokens[ i ], {} )
h := oP:Parse()
NEXT
NEXT
t1 := hb_MilliSeconds()
nT2 := t1 - t0
? " TSqlParser2 (Pratt) : " + Str( nT2, 8 ) + " ms (" + ;
Str( nT2 * 1000 / ( ITERS * Len( aSQL ) ), 6, 1 ) + " us/parse)"
?
IF nT2 > 0 .AND. nT1 > 0
IF nT1 > nT2
? " Pratt is " + Str( nT1 / nT2, 5, 2 ) + "x faster"
ELSEIF nT2 > nT1
? " Original is " + Str( nT2 / nT1, 5, 2 ) + "x faster"
ELSE
? " Same speed"
ENDIF
ENDIF
? "================================================================"
RETURN