Files
five/compiler/parser/exprreg.go
Charles KWON OhJun 59568f3301 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>
2026-03-31 09:41:50 +09:00

259 lines
6.5 KiB
Go

// 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()
}