Turun rekursif bekerja secara ideal ketika Anda dapat membuat keputusan tentang sepotong kode untuk diuraikan menggunakan konteks dan token saat ini.
Ekspresi merusak gambar : postfix, infix, dan lainnya. Masalah: Anda tidak dapat memahami jenis ekspresi apa yang Anda proses sampai Anda menguraikan bagian pertama. Seringkali prioritas operasi dan asosiatifitasnya juga penting bagi Anda sehingga AST yang dibangun memiliki struktur yang benar.
Pada artikel ini, kita akan menulis parser untuk dialek Go, fitur-fitur yang akan kita bahas nanti. Seperti yang Anda lihat, algoritma Pratt memecahkan sebagian besar masalah kami.

Terminologi
. , , - .
: , (, ).
: , .
/expression: , . : .
Statement: , . : .
/precedence: .
: , . : x++
.
: , . : ++x
.
: , . : x+y
.
/left-associative: .
/right-associative: .
non-associative: .
: , AST .
AST: , .
/: , .
: .
: .
: ().
: , .
: , .
Go ++ dan ++ Go
Agar tidak membuang waktu pada analisis leksikal , kami akan mengambil semua yang kami butuhkan dari perpustakaan standar:
, , :
type Token struct {
kind token.Token
value string
}
scanner.Scanner lexer
, :
go/ast, , .
Go right-associative , .
Go — statement, expression. . , .
. , , .
. , README : github.com/richardjennings/prattparser.
, .
type exprNode interface {
expr()
}
type nameExpr struct {
Value string
}
type prefixExpr struct {
Op token.Token
Arg exprNode
}
func (e *nameExpr) expr() {}
func (e *prefixExpr) expr() {}
, parseExpr()
, :
func (p *exprParser) parseExpr() exprNode {
tok := p.lexer.Consume()
switch tok.kind {
case token.IDENT:
return p.parseName(tok)
case token.ADD, token.SUB:
return p.parsePrefixExpr(tok)
case token.LPAREN:
return p.parseParenExpr(tok)
}
}
func (p *exprParser) parseName(tok Token) exprNode {
return &nameExpr{Value: tok.value}
}
func (p *exprParser) parsePrefixExpr(tok Token) exprNode {
arg := p.parseExpr()
return &prefixExpr{Op: tok.kind, Arg: arg}
}
- .
, . , -, parsePrefixExpr()
, map
, . , , map
.
, prefixParselet
:
type prefixParselet func(Token) exprNode
map[token.Token]prefixParselet
. :
func newExprParser() *exprParser {
p := &exprParser{
prefixParselets: make(map[token.Token]prefixParselet),
}
prefixExpr := func(kinds ...token.Token) {
for _, kind := range kinds {
p.prefixParselets[kind] = p.parsePrefixExpr
}
}
p.prefixParselets[token.IDENT] = p.parseName
prefixExpr(token.ADD, token.SUB)
return p
}
prefixExpr()
. , (. ).
func (p *exprParser) parseExpr() exprNode {
tok := p.lexer.consume()
prefix, ok := p.prefixParselets[tok.kind]
if !ok {
}
return prefix(tok)
}
, x+y
.
nameExpr{Value:"x"}
, +
. , .
infixParselet
, prefixParselet
, Expr
, . x+y
x
.
type infixParselet func(left exprNode, tok Token) exprNode
map
. , .
, +
-
binaryExpr
:
func (p *exprParser) parseBinaryExpr(left exprNode, tok token) exprNode {
right := p.parseExpr()
return &binaryExpr{Op: tok.kind, Left: left, Right: right}
}
parseExpr()
:
func (p *exprParser) parseExpr() exprNode {
tok := p.lexer.Consume()
prefix, ok := p.prefixParselets[tok.kind]
if !ok {
}
left := prefix(tok)
tok = p.lexer.Peek()
infix, ok := p.infixParselets[tok.kind]
if !ok {
return left
}
p.lexer.Consume()
return infix(left, tok)
}
:
- -:
x-y-z
=> x-(y-z)
- :
x*y+z
=> x*(y+z)
, .
{token.Token => precedence}
. , , :
p.prefixPrecedenceTab = map[token.Token]int{
token.ADD: 4,
token.SUB: 4,
}
p.infixPrecedenceTab = map[token.Token]int{
token.ADD: 2,
token.SUB: 2,
token.MUL: 3,
token.QUO: 3,
}
parseExpr()
precedence
:
func (p *exprParser) parseExpr(precedence int) exprNode {
tok := p.lexer.Consume()
prefix, ok := p.prefixParselets[tok.kind]
if !ok {
}
left := prefix(tok)
for precedence < p.infixPrecedenceTab[p.lexer.Peek().kind] {
tok := p.lexer.Consume()
infix := p.infixParselets[tok.kind]
left = infix(left, tok)
}
return left
}
, , , AST.
precedence
() parseExpr()
:
func (p *exprParser) parseBinaryExpr(left exprNode, tok Token) exprNode {
right := p.parseExpr(p.infixPrecedenceTab[tok.kind])
return &binaryExpr{Op: tok.kind, Left: left, Right: right}
}
parseBinaryExpr()
-. - , 1 :
func (p *exprParser) rparseBinaryExpr(left exprNode, tok Token) exprNode {
right := p.parseExpr(p.infixPrecedenceTab[tok.kind] - 1)
return &binaryExpr{Op: tok.kind, Left: left, Right: right}
}
, <<
-, rparseBinaryExpr()
.
— infixParselet
, '('
parseExpr(0)
, ')'
.
() — prefixParselet
, '('
parseExpr(0)
, ')'
.
func (p *exprParser) parseParenExpr(tok Token) exprNode {
x := p.parseExpr(0)
p.expect(token.RPAREN)
return x
}
— infixParselet
:
func (p *exprParser) parsePostfixExpr(left exprNode, tok Token) exprNode {
return &postfixExpr{Op: tok.kind, Arg: left}
}
, , .
, , , helper- precedence
:
prefixExpr := func(precedence int, kinds ...token.Token) {
for _, kind := range kinds {
p.prefixParselets[kind] = p.parsePrefixExpr
p.prefixPrecedenceTab[kind] = precedence
}
}
:
addPrefixParselet(token.LPAREN, 0, p.parseParenExpr)
addPrefixParselet(token.IDENT, 0, p.parseNameExpr)
leftAssocBinaryExpr(1, token.LOR)
leftAssocBinaryExpr(2, token.LAND)
leftAssocBinaryExpr(3,
token.EQL,
token.NEQ,
)
rightAssocBinaryExpr(4,
token.SHL,
)
leftAssocBinaryExpr(4,
token.ADD,
token.SUB,
token.SHR,
)
leftAssocBinaryExpr(5,
token.MUL,
token.QUO,
token.REM,
)
prefixExpr(6,
token.ADD,
token.SUB,
token.INC,
token.DEC,
)
postfixExpr(7,
token.INC,
token.DEC,
)
addInfixParselet(token.LPAREN, 8, p.parseCallExpr)
, , PrecAdd=3
, PrecMult=4
.
map
.
, , newExprParser
. prefixParselet
infixParslet
, — *exprParser
.
, , . , : , — .
Pratt Parsers: Expression Parsing Made Easy (Bob Nystrom). , , , , ", ".
, .
github.com/quasilyte/pratt-parsers-go.
: github.com/richardjennings/prattparser.
: