Five v0.9 — Harbour + Go fusion language
- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
258
compiler/parser/exprreg.go
Normal file
258
compiler/parser/exprreg.go
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
||||
// All rights reserved.
|
||||
|
||||
// exprreg.go — Expression parser registries for Pratt parser.
|
||||
//
|
||||
// Three registries:
|
||||
// prefixParsers — unary prefix: -, !, ++, --, <-, ASYNC, AWAIT
|
||||
// postfixParsers — postfix: (), [], :, ., ?:, ++, --, ->
|
||||
// primaryParsers — atoms: INT, STRING, IDENT, (, {, ::
|
||||
//
|
||||
// Adding a new operator = one line in init().
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"five/compiler/ast"
|
||||
"five/compiler/token"
|
||||
)
|
||||
|
||||
// PrefixParser parses a prefix unary expression.
|
||||
type PrefixParser func(p *Parser) ast.Expr
|
||||
|
||||
// PostfixParser parses a postfix expression given the left-hand side.
|
||||
type PostfixParser func(p *Parser, x ast.Expr) ast.Expr
|
||||
|
||||
// PrimaryParser parses an atomic/primary expression.
|
||||
type PrimaryParser func(p *Parser) ast.Expr
|
||||
|
||||
var (
|
||||
prefixParsers map[token.Kind]PrefixParser
|
||||
postfixParsers map[token.Kind]PostfixParser
|
||||
primaryParsers map[token.Kind]PrimaryParser
|
||||
)
|
||||
|
||||
func init() {
|
||||
prefixParsers = map[token.Kind]PrefixParser{
|
||||
token.MINUS: prefixUnary(token.MINUS),
|
||||
token.PLUS: prefixPlus,
|
||||
token.NOT: prefixUnary(token.NOT),
|
||||
token.INC: prefixUnary(token.INC),
|
||||
token.DEC: prefixUnary(token.DEC),
|
||||
token.ARROW_LEFT: prefixChanRecv,
|
||||
token.ASYNC_KW: prefixAsync,
|
||||
token.AWAIT_KW: prefixAwait,
|
||||
token.AT: prefixRef,
|
||||
}
|
||||
|
||||
postfixParsers = map[token.Kind]PostfixParser{
|
||||
token.LPAREN: postfixCall,
|
||||
token.LBRACKET: postfixIndex,
|
||||
token.COLON: postfixSend,
|
||||
token.QMARK: postfixNilSafe,
|
||||
token.DOT: postfixDot,
|
||||
token.ARROW: postfixAlias,
|
||||
token.INC: postfixIncDec(token.INC),
|
||||
token.DEC: postfixIncDec(token.DEC),
|
||||
token.COLONCOLON: postfixSelfStop,
|
||||
}
|
||||
|
||||
primaryParsers = map[token.Kind]PrimaryParser{
|
||||
token.INT: primaryLiteral,
|
||||
token.LONG: primaryLiteral,
|
||||
token.DOUBLE: primaryLiteral,
|
||||
token.STRING: primaryLiteral,
|
||||
token.DATE_LIT: primaryLiteral,
|
||||
token.TRUE: primaryLiteral,
|
||||
token.FALSE: primaryLiteral,
|
||||
token.NIL_LIT: primaryLiteral,
|
||||
|
||||
token.COLONCOLON: primarySelf,
|
||||
token.LPAREN: primaryParen,
|
||||
token.IF: primaryIf,
|
||||
token.IDENT: primaryIdent,
|
||||
token.AMPERSAND: primaryMacro,
|
||||
token.COLON: primaryWithSend,
|
||||
token.LBRACE: primaryArrayOrBlock,
|
||||
}
|
||||
}
|
||||
|
||||
// --- Prefix parsers ---
|
||||
|
||||
func prefixUnary(op token.Kind) PrefixParser {
|
||||
return func(p *Parser) ast.Expr {
|
||||
tok := p.advance()
|
||||
x := p.parseUnaryExpr()
|
||||
return &ast.UnaryExpr{OpPos: tok.Pos, Op: op, X: x}
|
||||
}
|
||||
}
|
||||
|
||||
func prefixPlus(p *Parser) ast.Expr {
|
||||
p.advance() // unary plus — no-op
|
||||
return p.parseUnaryExpr()
|
||||
}
|
||||
|
||||
func prefixChanRecv(p *Parser) ast.Expr {
|
||||
pos := p.advance().Pos
|
||||
ch := p.parsePostfixExpr()
|
||||
return &ast.ChanRecvExpr{ArrowPos: pos, Chan: ch}
|
||||
}
|
||||
|
||||
func prefixAsync(p *Parser) ast.Expr {
|
||||
pos := p.advance().Pos
|
||||
call := p.parsePostfixExpr()
|
||||
return &ast.AsyncExpr{AsyncPos: pos, Call: call}
|
||||
}
|
||||
|
||||
func prefixAwait(p *Parser) ast.Expr {
|
||||
pos := p.advance().Pos
|
||||
future := p.parsePostfixExpr()
|
||||
return &ast.AwaitExpr{AwaitPos: pos, Future: future}
|
||||
}
|
||||
|
||||
func prefixRef(p *Parser) ast.Expr {
|
||||
op := p.advance()
|
||||
x := p.parseUnaryExpr()
|
||||
return &ast.RefExpr{AtPos: op.Pos, X: x}
|
||||
}
|
||||
|
||||
// --- Postfix parsers ---
|
||||
|
||||
func postfixCall(p *Parser, x ast.Expr) ast.Expr {
|
||||
lp := p.advance().Pos
|
||||
var args []ast.Expr
|
||||
if !p.at(token.RPAREN) {
|
||||
args = p.parseExprList()
|
||||
}
|
||||
rp := p.expect(token.RPAREN).Pos
|
||||
return &ast.CallExpr{Func: x, LParen: lp, Args: args, RParen: rp}
|
||||
}
|
||||
|
||||
func postfixIndex(p *Parser, x ast.Expr) ast.Expr {
|
||||
lb := p.advance().Pos
|
||||
|
||||
// Slice syntax detection
|
||||
if p.isSliceSyntax() {
|
||||
var low, high ast.Expr
|
||||
if !p.at(token.COLON) {
|
||||
low = p.parseSliceIndex()
|
||||
}
|
||||
p.expect(token.COLON)
|
||||
if !p.at(token.RBRACKET) {
|
||||
high = p.parseSliceIndex()
|
||||
}
|
||||
rb := p.expect(token.RBRACKET).Pos
|
||||
return &ast.SliceExpr{X: x, LBracket: lb, Low: low, High: high, RBracket: rb}
|
||||
}
|
||||
|
||||
// Normal array index
|
||||
index := p.parseExpr()
|
||||
rb := token.Position{}
|
||||
for p.match(token.COMMA) {
|
||||
rb = p.current.Pos
|
||||
x = &ast.IndexExpr{X: x, LBracket: lb, Index: index, RBracket: rb}
|
||||
index = p.parseExpr()
|
||||
lb = rb
|
||||
}
|
||||
rb = p.expect(token.RBRACKET).Pos
|
||||
return &ast.IndexExpr{X: x, LBracket: lb, Index: index, RBracket: rb}
|
||||
}
|
||||
|
||||
func postfixDot(p *Parser, x ast.Expr) ast.Expr {
|
||||
if p.peekLitAt(1) != "" {
|
||||
dotPos := p.advance().Pos
|
||||
member := p.advance()
|
||||
return &ast.DotExpr{X: x, DotPos: dotPos, Member: member.Literal}
|
||||
}
|
||||
return nil // signal: stop postfix loop
|
||||
}
|
||||
|
||||
func postfixIncDec(op token.Kind) PostfixParser {
|
||||
return func(p *Parser, x ast.Expr) ast.Expr {
|
||||
opPos := p.advance().Pos
|
||||
return &ast.PostfixExpr{X: x, OpPos: opPos, Op: op}
|
||||
}
|
||||
}
|
||||
|
||||
func postfixSelfStop(p *Parser, x ast.Expr) ast.Expr {
|
||||
return nil // :: after expression — stop
|
||||
}
|
||||
|
||||
// postfixNilSafe and postfixSend/postfixAlias are complex — kept in expr.go
|
||||
// They call back into the main parser methods.
|
||||
|
||||
func postfixNilSafe(p *Parser, x ast.Expr) ast.Expr {
|
||||
if p.peekAt(1) != token.COLON {
|
||||
return nil // bare ? = QOut, not postfix
|
||||
}
|
||||
p.advance() // consume ?
|
||||
qpos := p.advance().Pos // consume :
|
||||
methodName := p.expectMethodName().Literal
|
||||
var args []ast.Expr
|
||||
hasParens := false
|
||||
if p.at(token.LPAREN) {
|
||||
hasParens = true
|
||||
p.advance()
|
||||
if !p.at(token.RPAREN) {
|
||||
args = p.parseExprList()
|
||||
}
|
||||
p.expect(token.RPAREN)
|
||||
}
|
||||
return &ast.NilSafeExpr{X: x, QPos: qpos, Method: methodName, Args: args, HasParens: hasParens}
|
||||
}
|
||||
|
||||
func postfixAlias(p *Parser, x ast.Expr) ast.Expr {
|
||||
arrowPos := p.advance().Pos
|
||||
field := p.parsePrimaryExpr()
|
||||
return &ast.AliasExpr{Alias: x, ArrowPos: arrowPos, Field: field}
|
||||
}
|
||||
|
||||
func postfixSend(p *Parser, x ast.Expr) ast.Expr {
|
||||
return p.parsePostfixSend(x)
|
||||
}
|
||||
|
||||
// --- Primary parsers ---
|
||||
|
||||
func primaryLiteral(p *Parser) ast.Expr {
|
||||
tok := p.advance()
|
||||
return &ast.LiteralExpr{ValuePos: tok.Pos, Kind: tok.Kind, Value: tok.Literal}
|
||||
}
|
||||
|
||||
func primaryParen(p *Parser) ast.Expr {
|
||||
p.advance()
|
||||
expr := p.parseExpr()
|
||||
for p.match(token.COMMA) {
|
||||
expr = p.parseExpr()
|
||||
}
|
||||
p.expect(token.RPAREN)
|
||||
return expr
|
||||
}
|
||||
|
||||
func primaryIf(p *Parser) ast.Expr {
|
||||
if p.peekAt(1) == token.LPAREN {
|
||||
return p.parseIIF()
|
||||
}
|
||||
p.error("expected expression, got IF")
|
||||
tok := p.advance()
|
||||
return &ast.LiteralExpr{ValuePos: tok.Pos, Kind: token.NIL_LIT, Value: "NIL"}
|
||||
}
|
||||
|
||||
func primaryIdent(p *Parser) ast.Expr {
|
||||
return p.parsePrimaryIdent()
|
||||
}
|
||||
|
||||
func primaryMacro(p *Parser) ast.Expr {
|
||||
return p.parseMacro()
|
||||
}
|
||||
|
||||
func primaryWithSend(p *Parser) ast.Expr {
|
||||
return p.parsePrimaryWithSend()
|
||||
}
|
||||
|
||||
func primaryArrayOrBlock(p *Parser) ast.Expr {
|
||||
return p.parseArrayOrBlock()
|
||||
}
|
||||
|
||||
func primarySelf(p *Parser) ast.Expr {
|
||||
return p.parsePrimarySelf()
|
||||
}
|
||||
Reference in New Issue
Block a user