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>
This commit is contained in:
@@ -591,6 +591,11 @@ func (p *Parser) parseClassDecl() *ast.ClassDecl {
|
||||
p.advance() // skip keyword
|
||||
p.advance() // skip :
|
||||
p.skipNewlines()
|
||||
} else if upper == "CLASSDATA" || upper == "CLASSVAR" {
|
||||
// CLASSDATA / CLASSVAR — class-level variable (treat as DATA)
|
||||
p.tokens[p.pos].Kind = token.DATA
|
||||
p.current = p.tokens[p.pos]
|
||||
members = append(members, p.parseDataDecl())
|
||||
} else if upper == "CLASS" {
|
||||
// CLASS VAR — class-level variable
|
||||
p.advance() // skip CLASS
|
||||
@@ -1511,6 +1516,8 @@ func (p *Parser) parseUse() *ast.UseCmd {
|
||||
pos := p.expect(token.USE).Pos
|
||||
var file ast.Expr
|
||||
var via, alias string
|
||||
var aliasExprNode ast.Expr
|
||||
var shared, readOnly bool
|
||||
|
||||
// USE without args = close
|
||||
if p.current.Kind != token.NEWLINE && p.current.Kind != token.EOF {
|
||||
@@ -1520,8 +1527,22 @@ func (p *Parser) parseUse() *ast.UseCmd {
|
||||
p.expectEndOfStmt()
|
||||
return &ast.UseCmd{UsePos: pos}
|
||||
}
|
||||
file = p.parseExpr()
|
||||
p.consumeFileExtension(file)
|
||||
// Bare ident as filename: USE myfile / USE myfile.dbf / USE myfile NEW
|
||||
// In Harbour, USE <name> treats name as a filename string, not a variable.
|
||||
// Only use parseExpr for parenthesized (USE (expr)) or string literal (USE "file").
|
||||
if p.at(token.IDENT) {
|
||||
// Check if it's a bare filename (ident optionally followed by .ext)
|
||||
name := p.advance().Literal
|
||||
if p.at(token.DOT) && (p.peekAt(1) == token.IDENT || p.peekAt(1) == token.INT) {
|
||||
p.advance() // skip DOT
|
||||
ext := p.advance().Literal
|
||||
name = name + "." + ext
|
||||
}
|
||||
file = &ast.LiteralExpr{ValuePos: pos, Kind: token.STRING, Value: name}
|
||||
} else {
|
||||
file = p.parseExpr()
|
||||
p.consumeFileExtension(file)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse optional clauses: VIA, ALIAS, EXCLUSIVE, SHARED, NEW, READONLY
|
||||
@@ -1530,20 +1551,44 @@ func (p *Parser) parseUse() *ast.UseCmd {
|
||||
upper := p.currentUpper()
|
||||
if upper == "VIA" {
|
||||
p.advance()
|
||||
via = p.expectMethodName().Literal
|
||||
if p.at(token.STRING) {
|
||||
via = p.current.Literal
|
||||
p.advance()
|
||||
} else {
|
||||
via = p.expectMethodName().Literal
|
||||
}
|
||||
continue
|
||||
}
|
||||
if upper == "ALIAS" {
|
||||
p.advance()
|
||||
if p.at(token.AMPERSAND) {
|
||||
p.parseMacro() // macro alias — skip
|
||||
} else if p.at(token.LPAREN) {
|
||||
// ALIAS ( expr ) — parenthesized alias expression (runtime)
|
||||
p.advance() // skip (
|
||||
aliasExpr := p.parseExpr()
|
||||
p.expect(token.RPAREN)
|
||||
if lit, ok := aliasExpr.(*ast.LiteralExpr); ok && lit.Kind == token.STRING {
|
||||
alias = lit.Value // constant string — store directly
|
||||
} else {
|
||||
aliasExprNode = aliasExpr // dynamic — evaluate at runtime
|
||||
}
|
||||
} else {
|
||||
alias = p.expectMethodName().Literal
|
||||
}
|
||||
continue
|
||||
}
|
||||
if upper == "EXCLUSIVE" || upper == "SHARED" || upper == "NEW" || upper == "READONLY" ||
|
||||
upper == "ADDITIVE" {
|
||||
if upper == "SHARED" {
|
||||
shared = true
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
if upper == "READONLY" {
|
||||
readOnly = true
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
if upper == "EXCLUSIVE" || upper == "NEW" || upper == "ADDITIVE" {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
@@ -1557,6 +1602,16 @@ func (p *Parser) parseUse() *ast.UseCmd {
|
||||
p.advance()
|
||||
if p.at(token.AMPERSAND) {
|
||||
p.parseMacro()
|
||||
} else if p.at(token.LPAREN) {
|
||||
// ALIAS ( expr ) — parenthesized alias expression
|
||||
p.advance()
|
||||
ae := p.parseExpr()
|
||||
p.expect(token.RPAREN)
|
||||
if lit, ok := ae.(*ast.LiteralExpr); ok && lit.Kind == token.STRING {
|
||||
alias = lit.Value
|
||||
} else {
|
||||
aliasExprNode = ae
|
||||
}
|
||||
} else {
|
||||
alias = p.expectMethodName().Literal
|
||||
}
|
||||
@@ -1571,7 +1626,7 @@ func (p *Parser) parseUse() *ast.UseCmd {
|
||||
}
|
||||
|
||||
p.expectEndOfStmt()
|
||||
return &ast.UseCmd{UsePos: pos, File: file, Via: via, Alias: alias}
|
||||
return &ast.UseCmd{UsePos: pos, File: file, Via: via, Alias: alias, AliasExpr: aliasExprNode, Shared: shared, ReadOnly: readOnly}
|
||||
}
|
||||
|
||||
func (p *Parser) parseSelect() *ast.SelectCmd {
|
||||
|
||||
Reference in New Issue
Block a user