ð íŽë¹ í¬ì€í
ì ë°ë°ë¥ë¶í° ë§ëë ìží°íëŠ¬í° in go ì±
ì ìœê³ ê°ìžì ìž ì 늬 목ì íì ìì±ë êžì
ëë€. 볞 í¬ì€í
ì ì¬ì©ë ìë£ë 몚ë 볞ìžìŽ ì§ì ì¬êµ¬ì±íì¬ ìì±íììì ì늜ëë€.

ìŽë² í¬ì€í ììë ì§ì í¬ì€í ê¹ì§ íŽì ë§ë€ìë ì°ëŠ¬ë§ì íìì ííìì íì±í ì ìë êž°ë¥ì íì¬íŽë³Œ ê²ìŽë€. ìœëë ë²šë¡ ìì볎Ʞì ìì ííì íì±ìŽëŒë ê²ì 구íí ë ììëìŽìŒí ì¬ì ê°ë ëª ê°ì§ì ê³ ë €ì¬íì ëíŽì ì§ê³ ëìŽê°ë³Žì.
1. ííì íì±ì íêž° ì ì..
ì§ì í¬ì€í ìì 구ííë let 묞, return 묞 íì±ì let ëë return 묞 ë€ìì ìŽë€ í í°ë€ìŽ ë±ì¥í ì§ ëª ííë€. íì§ë§ ííì íì±ì let, return 묞 íì±ì²ëŒ ìœê² 구íí ë§íŒì ìëë©° ꜀ ê¹ë€ë¡ìŽ ìì ìŽë€.
ê°ì¥ 뚌ì ííìì íì±í ë ê³ ë €íŽìŒ í ì¬íì ì°ì°ì ì°ì ìì(operator precedence)ìŽë€. ìíìì ì¬ì¹ì°ì°ìŽëŒë 죌ì ê° ìë€. ë§ì 곌 ëºì ì ê³±ì 곌 ëëì 곌 ê°ìŽ ë±ì¥íê² ëë©Ž ê³±ì 곌 ëëì ì ë§ì 곌 ëºì ë³Žë€ ëšŒì ê³ì°ëìŽìŒ íë€ë ì¬ì€ì ëë¶ë¶ì ì¬ëë€ìŽ ìê³ ìì ê²ìŽë€. ìŽë¬í ìì¹ìŽ íë¡ê·žëë° ìžìŽììë ë¹ì°í ì ì©ëìŽìŒ íë€. ë¿ë§ ìëëŒ ë§ì 곌 ê³±ì ìŽ ê°ìŽ ë±ì¥íì§ë§ ë§ì ì°ì°ì ìêŽížë¡ ê°ìžì ž ììŒë©Ž ë§ì ìŽ ëšŒì ìíëìŽìŒ íë€. ììë ë€ì곌 ê°ë€.
(5 + 10) * 2
ííìì íì±í ë ê³ ë €íŽìŒí ë ë€ë¥ž ì¬íì ê°ì íì ì í í°ë€ìŽ ì¬ë¬ ìì¹ì ëíë ì ìë€ë ì ìŽë€. ì륌 ë€ìŽ, ë€ì곌 ê°ì ìœëê° ìë€ê³ íŽë³Žì.
-5 - 10
5 * (add(2, 3) + 10)
첫ë²ì§ž ì€ì ìœë륌 볎멎 뚌ì ë±ì¥íë - ëŒë êž°ížë ì ì ì°ì°ì륌 ì믞íë€. ìŠ 0ìì -5ë§íŒì 빌ë ì°ì°ìŽë€. íì§ë§ ë€ì - ëŒë êž°ížë ì€ì ì°ì°ìë¡ì -5 ì 10ìŽëŒë ë ê°ì ì«ì륌 ëºì íë ìí ì íë€. ë€ììŒë¡ ë ë²ì§ž ì€ ìœë륌 볎ì. ê°ì¥ ë°ê¹¥ì ìë ìêŽížë 귞룹 ííììŒë¡ì, ìŽ ìêŽíž ìì ìë ííìì 뚌ì ìííëŒë ì믞ìŽë€. ë°ë©Žì add(2, 3)ì ìë ìêŽížë add ëŒë íšìì ížì¶ ì°ì°ìë¡ì ížì¶ ííìì íŽë¹íë€. ìŽë ê² ê°ì íì ì í í°ìŽëŒë ì¬ë¬ ìì¹ì ëíëê² ë ë ê°êž°ì êž°ë¥ìŽ ë¬ëŒì§ë ê²ì 볌 ì ìë€. ì°ëŠ¬ì íìë ìŽë° ì ë ê³ ë €íë©Žì íì±ì ìííŽìŒ íë€.
ê·žë¬ë©Ž Monkey íë¡ê·žëë° ìžìŽë¥Œ ëììŒë¡ ì°ëŠ¬ë§ì íìê° íì±í ì ìë ííì ììë ëíì ìŒë¡ ë€ì곌 ê°ë€.
# ì ì ì°ì°ì
-5
!true
!false
# ì€ì ì°ì°ì
5 + 5
5 - 5
5 / 5
5 * 5
# ë¹êµ ì°ì°ì
foo == bar
foo != bar
foo < bar
foo > bar
# 귞룹 ííì
5 * (5 + 5)
((5 + 5) * 5) * 5
# ížì¶ ííì
add(2, 3)
add(add(2, 3), add(5, 10))
max(5, add(5, (5 * 5)))
# ìë³ì ííì
foo * bar / foobar
add(foo, bar)
# íšì 늬í°ëŽ ííì
fn(x, y) {return x + y}(5, 5)
(fn(x) { return x }(5) + 10 ) * 10
# if ííì
let result = if (10 > 5) { true } else { false };
ì°ëŠ¬ë ìì ê°ì ì¢ ë¥ë€ì ííìì íì±í ì ìëë¡ íêž° ìíŽì íìì ì°ì°ì ì°ì ìì íì± ë°©ë²ìž íë« íì±ì ì¬ì©íŽë³Œ ê²ìŽë€. íë« íì±ì íµì¬ì ìž êµ¬ìì "í í°ì íì±íšìì ì°êŽìí¬ ë, íŽë¹ í í°ìŽ ì€ììžì§ ì ììžì§ì ë°ëŒ ìë¡ ë€ë¥ž íì± íšìë¡ ì°êŽìí€ë ê²"ìŽë€.
2. AST ë žëì String ë©ìë ì¶ê°íêž°
볞격ì ìž íë« íì륌 구ííêž° ì ì ëë²ê¹ ì ìœê² í ì ìëë¡ êµ¬íí AST ë žë ìŠ, Node ëŒë ìží°íìŽì€ì String() ë©ìë륌 충족ìí¬ ì ìëë¡ êµ¬ì±íì. ìŽ String() ë©ìëë AST륌 구ì±íë ë žëì ëŽì©ë¬Œì ì¶ë ¥íšìŒë¡ìš ë€ë¥ž ë žëì ë¹êµë íê³ , ì°ëŠ¬ê° ìëíì¬ ë žë륌 ì 구ì±íëì§ë ìŽíŽë³Œ ì ìë€. Node ëŒë ìží°íìŽì€ë êž°ì¡Žì 구ííë let 묞, return 묞 ë žëì ìží°íìŽì€ ë¿ë§ ìëëŒ ìë³ì ë žëìž Identifier, ê·žëŠ¬ê³ ìŽë²ì ì¶ê°ë¡ 구íí ííì ë žëìë ì¶ê°íŽì£Œëë¡ íì.
// ast/ast.go
type Node interface {
TokenLiteral() string
String() string
}
(..ìëµ..)
func (p *Program) String() string {
var out bytes.Buffer
for _, s := range p.Statements {
out.WriteString(s.String())
}
return out.String()
}
func (i *Identifier) String() string { return i.Value }
func (ls *LetStatement) String() string {
var out bytes.Buffer
out.WriteString(ls.TokenLiteral() + " ")
out.WriteString(ls.Name.String())
out.WriteString(" = ")
if ls.Value != nil {
out.WriteString(ls.Value.String())
}
out.WriteString(";")
return out.String()
}
func (rs *ReturnStatement) String() string {
var out bytes.Buffer
out.WriteString(rs.TokenLiteral() + " ")
if rs.ReturnValue != nil {
out.WriteString(rs.ReturnValue.String())
}
out.WriteString(";")
return out.String()
}
func (es *ExpressionStatement) String() string {
if es.Expression != nil {
return es.Expression.String()
}
return ""
}
3. íë« íì 구ííêž°
ìŽì 볞격ì ìŒë¡ íë« íì륌 구ííŽë³Žì. ìì ìžêžíì§ë§ íë« íìì íµì¬ì "í í° íì 곌 íì± íšì륌 ì°êŽ ì§ë ê²"ìŽë€. ì°ëŠ¬ê° ê°ì¥ 뚌ì í ìŒì ì ì(prefix) íì± íšìì ì€ì(infix) íì± íšì륌 ê·žì ë§ë í í°ê³Œ ì°êŽ ì§ë ìŒìŽë€.
뚌ì ì°ëŠ¬ë ì ì ëë ì€ì íì±ê³Œ ì°êŽë í í°ìŽ ížì¶í ëììž ì ì, ì€ì íì± íšì íì ì ì ìíŽë³Žì.
// parser/parser.go
type (
prefixParseFn func() ast.Expression
infixParseFn func(ast.Expression) ast.Expression
)
prefixParseFn ìŽëŒë íì ì íšìë ìŽëŠìì ì ì¶í ì ìë€ìíŒ ì ì íì±ì ìííë íšìì íŽë¹íë€. ìŽë ì묎ê²ë ìžìë¡ ë°ì§ ìë, ííì íì ì ë°ííë€. ë°ë©Žì infixParseFn íì ì íšìë ì€ì íì±ì ìííë©°, ííì 1ê°ë¥Œ ìžìë¡ ì ë¬ ë°ëë€. ìŽ ìžìë¡ ì ë¬ ë°ë ííìì ì€ì ì°ì°ìì ì¢ìž¡ì ìë ííìì ì믞íë€. ì륌 ë€ìŽ, ìëì²ëŒ ì€ì ì°ì°ìê° ì¬ì©ëë ìì€ìœëê° ìë€ê³ íŽë³Žì.
2 * 5
ê·žë¬ë©Ž infixParseFn íì íšìì ìžììë ì ìì€ìœë êž°ì€ìŒë¡ 2 ëŒë ííììŽ ìžìë¡ ë€ìŽê°ë ì ìŽë€.
ìŽë ê² ì ì, ì€ì íì± íšì íì ì ì ìíìŒë ììŒë¡ ì ì ì°ì°ì ëë ì€ì ì°ì°ìì êŽë šë í í°ì ë§ëë©Ž ê·žì ë§ë íì± íšì륌 ížì¶í ì ìëë¡ íŽì£Œì. ì°ëŠ¬ë ìŽë¥Œ ìíŽ Parser 구조첎ì ë€ì곌 ê°ì map ìë£êµ¬ì¡°ë¥Œ ì¶ê°íì.
// parser/parser.go
type Parser struct {
l *lexer.Lexer
errors []string
curToken token.Token
peekToken token.Token
prefixParseFns map[token.TokenType]prefixParseFn
infixParseFns map[token.TokenType]infixParseFn
}
ê·žëŠ¬ê³ ìŽ map ìë£êµ¬ì¡°ì í¹ì í í°ê³Œ í¹ì íì± íšìê° ì ì¥ë ì ìê² íŽì£Œë íšìë ê°ìŽ ì ìíŽë³Žì.
// parser/parser.go
func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
p.prefixParseFns[tokenType] = fn
}
func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
p.infixParseFns[tokenType] = fn
}
3-1. ìë³ì ííì íì±íêž°
ê°ì¥ 뚌ì 구ííŽë³Œ êž°ë¥ì ìë³ì ííìì íì±íë ê²ìŽë€. ë€ì íë² ìë³ì ííììŽë ìŽë€ ìì€ìœë륌 ì믞íëì§ ëŠ¬ë§ìžë íŽë³Žì.
foobar;
add(foobar, barfoo);
foobar + barfoo;
if (foobar) {
(...ìëµ...)
}
ìë³ì ííìì ì íì±íëì§ í ì€ížíë ìœëë¶í° ìŽíŽë³Žì.
// parser/parser_test.go
func TestIdentifierExpression(t *testing.T) {
input := "foobar;"
l := lexer.New(input)
p := New(l)
// run lexing and parsing
program := p.ParseProgram()
if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements))
}
// type assertion: downcast from interface to struct that satisfies `Statement` interface
stmt, ok := program.Statements[0].(*ast.ExpressionStatement) // `[0]`ì ì ìŒí ëª
ë ¹ë¬žìŽ ëŽê²šìëì§ íìžíêž° ìíš
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", stmt)
}
ident, ok := stmt.Expression.(*ast.Identifier)
if !ok {
t.Fatalf("stmt.Expression is not ast.Identifier. got=%T", ident)
}
if ident.Value != "foobar" {
t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
}
if ident.TokenLiteral() != "foobar" {
t.Errorf("ident.TokenLiteral() not %s. got=%s", "foobar", ident.TokenLiteral())
}
}
í ì€íž ìœëë ìŽì 곌 ìì±í ê²ê³Œ í¬ê² ë€ë¥Žì§ ìë€. í ì€íž ìœëì íŽìì ë ììê² ë§¡êž°ë €ê³ íë€. ì²ì²í ìœìŽë³Žë©Žì ê·žëì ë°°ìŽ ëŽì©ì ë³µìµíë ê²ë ì¢ë€. íì¬ ì í ì€íž ìœë륌 ì€ííë©Ž ë¹ì°í ì€íšíë€. ìëíë©Ž ì°ëŠ¬ë ìì§ ìë³ì ííìì ì€ì§ì ìŒë¡ íì±íë ë¡ì§ì ìì±íì§ ììêž° ë묞ìŽë€. ìŽì 볞격ì ìŒë¡ ìì±íŽë³Žì.
ê°ì¥ 뚌ì í¹ì í í°ì ë°ëŒ switch-case 구묞ìŒë¡ ë¶êž°íŽì í¹ì 구묞ì íì±íëë¡ íë íšìì ìë³ì ííì ë¶êž°ë¥Œ ì¶ê°íŽë³Žëë¡ íì.
// parser/parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
case token.LET:
return p.parseLetStatement()
case token.RETURN:
return p.parseReturnStatement()
default:
return p.ParseExpressionStatement() // ìë¡ ì¶ê°ë íšì
}
}
ì ìì€ìœë륌 볎멎 ParseExpressionStatment ëŒë ìë¡ìŽ íšìê° ì¶ê°ëìë€. ìŽ íšìì ìí ì ë ìŽíŽë³Žì.
// parser/parser.go
func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
stmt := &ast.ExpressionStatement{Token: p.curToken}
stmt.Expression = p.parseExpression(LOWEST)
if p.peekTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}
ì íšìë ExpressionStatment ëŒë 구조첎륌 ìŽêž°ííê³ , íŽë¹ 구조첎ì Token ë©€ë²ì íŒìê° ë°ëŒë³Žê³ ìë íì¬ í í° ìŠ, IDENT ìŽëŒë í í°ì ë£ìŽì€ë€. ê·žëŠ¬ê³ íŽë¹ 구조첎ì Expression ë©€ë²ì ííìì ì€ì§ì ìŒë¡ íì±íë ìí ì íë parseExpression íšì륌 ížì¶íë€. ìŽ ë, LOWEST ëŒë ìžì륌 ë£ìŽì£Œëë°, ìŽ LOWESTë ë°ë¡ ë€ììŒë¡ ìŽíŽë³Œ ì°ì°ì ì°ì ìì ì€ ê°ì¥ ë®ì ì°ì°ìì륌 ì믞íë€.
ì°ì°ì ì°ì ìì륌 ì ìíŽë³Žì.
// parser/parser.go
const (
_ int = iota
LOWEST
EQUALS // ==
LESSGREATER // < >
SUM // +
PRODUCT // *
PREFIX // !
CALL // ížì¶ ì°ì°ìë¡ìì ìêŽíž(ex. add())
)
Go ìžìŽìë iotaëŒë Enumeratorê° ì¡Žì¬íë€. ìŽ iota륌 ì¬ì©íë©Ž ìì ê²œì° 0ììë¶í° ììíŽì 1ì© ìŠê°íë ìì ì ì륌 ë¶ì¬í ì ìê² ëë€. ê²°êµ, LOWESTë 1ìŽê³ ìëë¡ ëŽë €ê°ìë¡ 1ì© ìŠê°íì¬ CALLì 7ìŽ ë¶ì¬ëë€. ìŽë ì°ì°ì ì°ì ìì륌 ì믞íë©° ê°ìŽ ëììë¡ ì°ì°ì ì°ì ììê° ëë€. ê³ ë¡, ì§ì ìì€ìœëìì parseExpression íšìì ìžìì LOWEST륌 ëê²šì€ ê²ì ê°ì¥ ë®ì ì°ì°ì ì°ì ìì륌 ëê²šì€ ê²ìž ì ìŽë€.
ìŽì parseExpression íšìì ë¡ì§ì ìŽíŽë³Žì.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
return nil
}
leftExp := prefix() // call `prefixParseFn`
return leftExp
}
ìê¹ ì ìíë í í°ê³Œ ì ì íì±íšìë¡ êµ¬ì±ë map ìë£êµ¬ì¡°ìì íì¬ í í° íì ì ë§ë ì ì íì± íšì륌 ê°ì žìì íŽë¹ íšì륌 ížì¶íë€. ìŽ ížì¶íë ì ì íì± íšìì ìê¹ì륌 ìì§ì ìŽíŽë³Žì§ë ììì§ë§ leftExp ë³ììë ë¶ëª íì±ë ííì ë žëê° ì¡Žì¬í ê²ìŽë€.
ë°©êž ížì¶í ì ì íì± íšìì ìê¹ì륌 ìŽíŽë³Žêž° ìíŽì map ìë£êµ¬ì¡°ì í í°ê³Œ ê·žì ë§ë ì ì íì± íšì륌 ë±ë¡íŽë³Žì.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
p := &Parser{
l: l,
errors: []string{},
}
// íì¬ ìŽêž° token ê°ì ë¹ ë¬žììŽêž° ë묞ì, curToken, peekTokenì í í°ì ëŽìŒë €ë©Ž 2ë² ìí
p.nextToken()
p.nextToken()
// ì ì íì± íšìì ê·žì ë§ë ìë³ì í í°ì ë±ë¡
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
return p
}
// ast.Expression ìŽëŒë ìží°íìŽì€ë¥Œ ast.Identifier êµ¬ì¡°ì²Žê° ì¶©ì¡±íêž° ë묞ì ìŽì²ëŒ íí ê°ë¥
func (p *Parser) parseIdentifier() ast.Expression {
return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}
ë ì륌 ìžìë¡ ë£ìŽ íì륌 ë°ííë New íšììì map ìë£êµ¬ì¡°ë¥Œ ìŽêž°ííê³ , ì ì íì± íšìì ê·žì ë§ë IDENT(ìë³ì) í í°ì ì ì¥íë€. ê·žëŠ¬ê³ ìŽ ë ì ì íì± íšììž parseIdentifer íšìë ëšì§ Identifer 구조첎ì í¬ìží° ë³ì륌 ë°ííë€.
ê°ìžì ìŒë¡ go-langì 묞ë²ì ìŒë¡ ê¶êžíë ì ì Identifier 구조첎 í¬ìží° ë³ì륌 ë°ííëë°, ì íšì ìê·žëì²ìë ast.Expression ìží°íìŽì€ë¥Œ ëª ìíë ê¶êžíë€. ê²°êµìë ë°ííë 결곌묌ì 구첎ì ìž íì ì ì¢ ììí€ì§ ìê³ ì¶ìíë ë°©ì ìŠ, ížì¶ì ì ì¥ìì parseIdentifer íšì ë§ê³ ë ë€ë¥ž ì¢ ë¥ì íì± íšì륌 ížì¶í ë ëìŒí ìží°íìŽì€ë¡ ížì¶ìŽ ê°ë¥íëë¡ ë§ë€ìŽ 죌Ʞ ìíšìŽìë€. ìì§ go-langì ìµìíì§ ìì ìŽìíì§ë§ ì ìíŽ ëê°ë ìê°ìŽ íìí ë¯ íë€.
ì, ìŽì ìë³ì ííìì íì±í ì ìë Ʞ볞ì ìž ì€ë¹ê° ëìë€. ìê¹ ììì ìŽíŽë³ž í ì€íž ìœë륌 ìëì²ëŒ ì€ííŽë³Žì. í ì€íž ê²°ê³Œê° íµê³Œíë©Ž ì ë°ëŒìš ê²ìŽë€.
go test ./parser
3-2. ì ì 늬í°ëŽ ííì íì±íêž°
ë€ììŒë¡ íìê° íì±í ííìì ì ì 늬í°ëŽ ííììŽë€. ì ì 늬í°ëŽì ê° ì첎륌 ìì±íêž° ë묞ì ííìì íŽë¹íë€. ììë ë€ì곌 ê°ë€.
5;
ìì 겜ì°ë ì ì 늬í°ëŽë§ ì¡Žì¬íì§ë§, ìëì²ëŒ ì ì 늬í°ëŽìŽ ë€ë¥ž ííì곌 ìì¬ ì¡Žì¬í ìë ìë€.
let x = 5; # `5`ê° ì ì 늬í°ëŽ
add(5, 10); # `5` ì `10`ìŽ ì ì 늬í°ëŽ
5 + 5 + 5; # `5`ëŒë ì«ì ê°ê°ìŽ ì ì 늬í°ëŽ
ìŽë²ìë í ì€íž ìœë륌 뚌ì ìŽíŽë³Žì. ìŽ í ì€íž ìœëë ììŒë¡ ì°ëŠ¬ê° 구íí ì ì 늬í°ëŽ ííìì íìê° ì íì±íëì§ ì ê²íêž° ìí ìœëìŽë€.
// parser/parser_test.go
func TestIntegerLiteralExpression(t *testing.T) {
input := "5;"
l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain 1 statements. got=%d", program.Statements)
}
// type assertion: downcast from interface to struct(`ExpressionStatement`) that satisfies `Statement` interface
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
}
// type assertion: downcast from interface(`Expression`) to struct(`IntegerLiteral`) that satisfies `Expression` interface
literal, ok := stmt.Expression.(*ast.IntegerLiteral)
if literal.Value != 5 {
t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
}
if literal.TokenLiteral() != "5" {
t.Errorf("literal.TokenLiteral not %s. got=%s", "5", literal.TokenLiteral())
}
}
ìë³ì ííìì í ì€ížíë ìœëì ê±°ì ì ì¬íë ìœê° ë€ë¥žì ìŽ ìë€. ë°ë¡ ì ì 늬í°ëŽ ííìì ëíëŽë êµ¬ì¡°ì²Žë¡ type assertion íë ë¶ë¶ìŽë€. ìì§ì IntegerLiteral ìŽëŒë 구조첎륌 ì°ëŠ¬ê° ì ìíì§ë ììë€. ë°ë¡ ë€ìì ì ìí ê²ìŽë€. ìŽìšê±Ž IntegerLiteral ìŽëŒë êµ¬ì¡°ì²Žë¡ ë€ìŽìºì€í ì ìíí í, íŽë¹ 구조첎ì Value ë©€ë²ì ë€ìŽìë ê°ê³Œ TokenLiteral() ë©ìë륌 ížì¶íšìŒë¡ìš ë°íëë 묞ììŽì ë¹êµíë€. ìŽ ë 죌목í ì ì ìŽì 곌 ë€ë¥Žê² ìŽë²ìë Value ë©€ë²ì ë€ìŽìë ê°ìŽ 묞ìê° ìë ì€ì ì«ììžì§ë¥Œ ë¹êµíë€ë ì ìŽë€.
ìŽì IntegerLiteral 구조첎륌 ìëì²ëŒ ì ìíŽë³Žì.
// ast/ast.go
type IntegerLiteral struct {
Token token.Token
Value int64
}
func (il *IntegerLiteral) expressionNode() {}
func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal }
func (il *IntegerLiteral) String() string { return il.Token.Literal }
IntegerLiteral 구조첎 ìì 2ê°ì ë©€ë²ë¥Œ ê°ëë°, í¹ìŽí ì ì Value ë©€ë²ì íì ìŽ ë¬žììŽìŽ ìë int64ë¡ ì ìëìë€. ìŽì ìŽ Value ë©€ë²ìë ì ì 늬í°ëŽìŽ íííë 묞ìê° ê°ë ì€ì ê° ìŠ, ì ìê°ì ëŽì ê²ìŽë€.
ìŽì ì ì 늬í°ëŽì ëíŽì ì€ì§ì ìž íì±ì ìííë íšì륌 ì ìíŽìŒ íë€. ìë ìœë륌 볎ì.
// parser/parser.go
func (p *Parser) parseIntegerLiteral() ast.Expression {
lit := &ast.IntegerLiteral{Token: p.curToken}
// `base = 0`: 묞ììŽ ì ëì¬ì ë°ëŒ ìëìŒë¡ ì§ë²ì íëšíì¬ ì ìë¡ ë³í
value, err := strconv.ParseInt(p.curToken.Literal, 0, 64)
if err != nil {
msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Literal)
p.errors = append(p.errors, msg)
return nil
}
lit.Value = value
return lit
}
íµì¬ ë¡ì§ì 묞ììŽì ì ìíìŒë¡ ë³ííêž° ìíŽ strconv.ParseInt íšì륌 ì¬ì©íê³ , ë³í í, IntegerLiteral 구조첎ì Value ë©€ë²ì ë³íë ì ì ê°ì í ë¹íë€ë ì ìŽë€.
ìŽì ìŽë ê² ìì±í ì ì 늬í°ëŽì íì±íë íšì륌 í¹ì í í°ê³Œ ê·ž í í°ì ë§ë íì±íšìê° ëŽê²šìë map ìë£êµ¬ì¡°ì ë±ë¡íêž°ë§ íë©Ž ëë€. ìëì ê°ìŽ ë§ìŽë€.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
(... ìëµ ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral) // ìë¡ê² í í°ê³Œ íì±íšì륌 ë±ë¡!
return p
}
3-3. ì ì ì°ì°ì ííì íì±íêž°
ë€ììŒë¡ ìŽíŽë³Œ ë¶ë¶ì ì ì ì°ì°ì, ê·žëŠ¬ê³ ê·ž ì ì ì°ì°ìì íšê» ë±ì¥íë ííìì íì±í ì°šë¡ë€. ì ì ì°ì°ìë íŒì°ì°ì 1ê°ë¥Œ ê°ëë€. ê·žëŠ¬ê³ ìŽ íŒì°ì°ì륌 ê°ì§ê³ ííìì íì±íëë°, ìëì ê°ì êµ¬ì¡°ë¡ ííììŽ ë§ë€ìŽì§ë€.(ì°žê³ ë¡ íŽë¹ ì± ììë - ëë ! ëŒë ì ì ì°ì°ìë§ íì±í ì ìëë¡ ëìŽ ìë€. Ʞ볞ì ìŒë¡ + ì°ì°ìì ê°ì ê²ì ëí íì± êž°ë¥ìŽ ì ê³µëì§ë ìëë€)
# 구조
<ì ì ì°ì°ì><ííì>
# ìì
-5;
!foobar;
!myFunc(2);
-add(5, 5);
ìŽë²ìë ì ì ì°ì°ì ííì íì±ìŽ ì ëìíëì§ í ì€ížíë í ì€íž ìœëë¶í° ìŽíŽë³Žì.
// parser/parser_test.go
func TestParsingPrefixExpression(t *testing.T) {
prefixTests := []struct {
input string
operator string
integerValue int64
}{
{"!5;", "!", 5},
{"-15;", "-", 15},
}
for _, tt := range prefixTests {
l := lexer.New(tt.input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements))
}
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
}
exp, ok := stmt.Expression.(*ast.PrefixExpression)
if !ok {
t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
}
if exp.Operator != tt.operator {
t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
}
if !testIntegerLiteral(t, exp.Right, tt.IntegerValue) {
return
}
}
}
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
integ, ok := il.(*ast.IntegerLiteral)
if !ok {
t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
return false
}
if integ.Value != value {
t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
return false
}
if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
t.Errorf("integ.TokenLiteral not %d. got=%s", value, integ.TokenLiteral())
return false
}
return true
}
ì í ì€íž ìœëìì ìì§ ì ìžìŽ ìë ê²ë€ìŽ ìë ìíìŽë€. ì륌 ë€ìŽ, PrefixExpression ëŒë 구조첎ë ìì§ ì ìê° ìëìŽìì§ë§, ìŽ êµ¬ì¡°ì²Žë ì ì ì°ì°ì ííìì ëí ì 볎륌 ëŽê³ ìë 구조첎ìŽë€. ìŽì ìŽ PrefixExpression ìŽëŒë 구조첎륌 ì ìíŽë³Žì.
// ast/ast.go
type PrefixExpression struct {
Token token.Token
Operator string
Right Expression
}
func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal }
func (pe *PrefixExpression) String() string {
var out bytes.Buffer
out.WriteString("(")
out.WriteString(pe.Operator)
out.WriteString(pe.Right.String())
out.WriteString(")")
return out.String()
}
PrefixExpression 구조첎ë í¬ê² 3ê°ì§ ë©€ë²ë¥Œ ê°ì§ë€. 첫ë²ì§žë ë€ë¥ž 구조첎ë ëìŒíê² í í° ë©€ë²ë¥Œ ê°ì§ë€. ê·žëŠ¬ê³ Operatorë ì°ì°ì륌 ì믞íë€. ì ì ì°ì°ìë¡ë -, ! ê° ìë€. ìŽ ì°ì°ì 묞ì ìì²Žê° ë€ìŽê°ë€. ê·žëŠ¬ê³ Right ëŒë ë©€ë²ê° ìëë°, ìŽë ì ì ì°ì°ìì ìì¹ë¥Œ êž°ì€ìŒë¡ ì€ë¥žìªœì ìë ííìì ì믞ííë©° Expression ìŽëŒë ìží°íìŽì€ íì ìŒë¡ ì ìžëìë€. PrefixExpression 구조첎ë ìŽìšê±Ž AST ë žëë¡ êµ¬ì±ëìŽìŒ íêž° ë묞ì ë€ë¥ž 구조첎ì²ëŒ ìží°íìŽì€ë¥Œ 충족ìí€êž° ìíŽ ììì 3ê°ì§ ë©ìë륌 ì ìíë€.
ì¬êž°ì ì ì, parseExpression ë©ìëì í ê°ì§ ì¶ê°ì ìž ë¡ì§ìž ì¶ê°íŽë³Žëë¡ íì. ìŽ ì¶ê°ë ë¡ì§ì ì ì ì°ì°ì íšì륌 ì°Ÿì§ ëª»íì ë ëë²ê¹ íêž° ìœëë¡ ìœìì ë©ìžì§ë¥Œ ëŽë±ëë¡ íë€.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix() // call `prefixParseFn`
return leftExp
}
func (p *Parser) noPrefixParseFnError(t token.TokenType) {
msg := fmt.Sprintf("no prefix parse function for %s found", t)
p.errors = append(p.errors, msg)
}
ê·žë¬ë©Ž ìŽì ì ì ì°ì°ì ííìì ëí ì€ì§ì ìž íì±ì ìííë íšì륌 ìì±íŽë³Žì. ê·žëŠ¬ê³ ìŽ íì± íšì륌 í í°ê³Œ ê·ž í í°ì ë§ë íì± íšìê° ì ì¥ëìŽ ìë map ìë£êµ¬ì¡°ì ë±ë¡ììŒë³Žì.
// parser/parser.go
func (p *Parser) parsePrefixExpression() ast.Expression {
expression := &ast.PrefixExpression{
Token: p.curToken,
Operator: p.curToken.Literal,
}
p.nextToken()
expression.Right = p.parseExpression(PREFIX)
return expression
}
func New(l *lexer.Lexer) *Parser {
(... ìëµ ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression) // ì ì ì°ì°ì `!` ì¶ê°
p.registerPrefix(token.MINUS, p.parsePrefixExpression) // ì ì ì°ì°ì `-` ì¶ê°
return p
}
ì ìì€ìœëìì í¹ìŽí ì ì parsePrefixExpression íšì ëŽë¶ìì nextToken íšì륌 í ë² ížì¶íë€ë ì ìŽë€. ì ížì¶í ê¹? ìŽì ë¶í° ë§íë©Ž ì ì ì°ì°ì ì€ë¥žìªœì ìë ííììŒë¡ í í° ìì¹ë¥Œ ì®êž°êž° ìíšìŽë€. ìŠ, nextToken íšì륌 ížì¶íêž° ì ê¹ì§ì curTokenì ì ì ì°ì°ì(! í¹ì -)ìŒ ê²ìŽë€. íì§ë§ ì ì ì°ì°ìê° ë±ì¥íë©Ž ë°ëì ì€ë¥žìªœì ííììŽ ë±ì¥íêž° ë§ë šìŽë€. ë°ëŒì ìŽ ì€ë¥žìªœì ìë ííììŒë¡ íì¬ ìì¹ë¥Œ ì®êž°êž° ìíŽ nextToken íšì륌 í ë²ë§ ížì¶íë ê²ìŽë€. ê·žëŠ¬ê³ ë ë€ì parseExpression íšìë¡ ì€ë¥žìªœì ííìì íì±íë€.
ë€ë§, ì¬êž°ì parseExpression íšìì ìžìë¡ ì ë¬ëë PREFIX ìŠ, ì ì ì°ì°ìì ì°ì ìì ê°ìŽ 구첎ì ìŒë¡ ìŽë»ê² ëìíëì§ë ìì§ ììë³Žì§ ëª»íë€. ìŽë ë°ë¡ ë€ì ëª©ì°šìž ì€ì ì°ì°ììì ìŽíŽë³Œ ìì ìŽë€.
3-4. ì€ì ì°ì°ì ííì íì±íêž°
ë§ì§ë§ìŒë¡ ì€ì ì°ì°ì ííìì íì±íë êž°ë¥ì ì¶ê°íŽë³Žì. Monkey íë¡ê·žëë° ìžìŽìì ì€ì ííìì ìì ìì€ìœëë ë€ì곌 ê°ë€.
2 + 2;
2 - 2;
2 * 2;
2 / 2;
2 > 2;
2 < 2;
2 == 2;
2 != 2;
ì ìì€ìœëì íšíŽìì 볎멎 ìê² ì§ë§ ì€ì ì°ì°ì ííìì ë€ì곌 ê°ì 구조륌 ê°ëë€.
<ìŒìªœ íŒì°ì°ì> <ì€ì ì°ì°ì> <ì€ë¥žìªœ íŒì°ì°ì>
ììë ìŽë²ìë ì€ì ì°ì°ì ííìì ì íì±íëì§ í ì€ížíë í ì€íž ìœëë¶í° ìŽíŽë³Žì.
// parser/parser_test.go
func TestParsingInfixExpressions(t *testing.T) {
infixTests := []struct {
input string
leftValue int64
operator string
rightValue int64
}{
{"2 + 2;", 2, "+", 2},
{"2 - 2;", 2, "-", 2},
{"2 * 2;", 2, "*", 2},
{"2 / 2;", 2, "/", 2},
{"2 > 2;", 2, ">", 2},
{"2 < 2;", 2, "<", 2},
{"2 == 2;", 2, "==", 2},
{"2 != 2;", 2, "!=", 2},
}
for _, tt := range infixTests {
l := lexer.New(tt.input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements))
}
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
}
exp, ok := stmt.Expression.(*ast.InfixExpression)
if !ok {
t.Fatalf("stmt is not ast.InfixExpression. got=%T", stmt.Expression)
}
if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
return
}
if exp.Operator != tt.operator {
t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
}
if !testIntegerLiteral(t, exp.Right, tt.rightValue) {
return
}
}
}
í ì€íž ìœë ì첎ë ì ì ííì í ì€íž ìœëì ê±°ì ì ì¬íë€. ë€ë§, ì€ì ì°ì°ì ííìì ì¬ì©ëë ì€ì ì°ì°ìê° 2ê°ì íŒì°ì°ì륌 ê°ë ìŽí ì°ì°ììŽë€ 볎ë, ì°ì°ì륌 êž°ì€ìŒë¡ ìŒìªœ, ì€ë¥žìªœì ìë ê° ííìì ê²ìŠíë if êµ¬ë¬žë§ ì¶ê°ëìì ë¿ìŽë€.
ì í ì€íž ìœëìì 못볎ë êµ¬ì¡°ì²Žê° ëíëêž° ììíë€. ë°ë¡ ast.InfixExpression ìŽë€. ìŽì ìŽ êµ¬ì¡°ì²Žë¥Œ ì ìíŽë³Žì. ìŽ êµ¬ì¡°ì²Ž ìì AST ë žëë¡ ííëìŽìŒ íë¯ë¡ ë§ì°¬ê°ì§ë¡ Expression ìží°íìŽì€ë¥Œ 충족ììŒë³Žì.
// ast/ast.go
type InfixExpression struct {
Token token.Token
Left Expression
Operator string
Right Expression
}
func (ie *InfixExpression) expressionNode() {}
func (ie *InfixExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *InfixExpression) String() string {
var out bytes.Buffer
out.WriteString("(")
out.WriteString(ie.Left.String())
out.WriteString(" "+ ie.Operator +" ")
out.WriteString(ie.Right.String())
out.WriteString(")")
return out.String()
}
InfixExpression 구조첎ì ë©€ë²ë¥Œ 볎멎 ì ì ííìì ëíëŽë êµ¬ì¡°ì²Žìž PrefixExpression곌 ë¬ëŠ¬ Left ë©€ë²ê° ì¶ê°ëììì 볌 ì ìë€.
ìŽì ì°ëŠ¬ë ì€ì ì°ì°ì ííììŽ ë±ì¥íì ë, íì±íŽì€ ë¡ì§ì ìì±íŽë³Žëë¡ íì. ê°ì¥ 뚌ì ìì±í ìœëë ì€ì ì°ì°ì륌 íì±íë íì± íšì륌 ì ìíê³ ë±ë¡ììŒìŒ íë€. ë€ë§, ìŽ ë 죌ìí ì ì ìŽë€ ì°ì°ì í í°ì ë§ë¬ì ë ì ì ì°ì°ì íì± íšì륌 ê°ì žì¬ ê²ìžì§, ì€ì ì°ì°ì íì± íšì륌 ê°ì žì¬ ê²ìžì§ ì íëšíŽìŒ íë€. ëšì ìž ìë¡, - ì°ì°ìê° ë±ì¥íì ë íëì ëª ë ¹ë¬ž ëŽì ìë¡ ë€ë¥ž êž°ë¥ì íë íì± íšì륌 ê°ì žììŒ í ëê° ìë€.
-5 - 3;
ê°ì¥ ìì ìë - ë ë¶ëª ì ì ì°ì°ììŽê³ , ìŽë¥Œ íì±í ëë ì ì ì°ì°ì íì± íšì륌 ê°ì žììŒ íë€. íì§ë§ ê°ìŽë°ì ìë - ë ì€ì ì°ì°ììŽê³ , ì€ì ì°ì°ì íì± íšì륌 ê°ì žììŒ íë€. ë°ë¡ ìŽë° 겜ì°ë¥Œ ì 구ë¶íŽì£ŒìŽìŒ íë€ë ê²ìŽë€. ë°ìì ìê°íê² ì§ë§ ìŽë¬í 구ë¶ì ì°ì°ì ì°ì ìì륌 íì©í ê²ìŽë€. ë°ëŒì í¹ì ì°ì°ìì ì°ì ìì륌 ì ì¥íë map ìë£êµ¬ì¡°ë¥Œ ìë¡ê² íë ì ìíŽë³Žì.
// parser/parser.go
var precedences = map[token.TokenType]int {
token.EQ: EQUALS,
token.NOT_EQ: EQUALS,
token.LT: LESSGREATER,
token.GT: LESSGREATER,
token.PLUS: SUM,
token.MINUS: SUM,
token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT,
}
ì map ìë£êµ¬ì¡°ì valueì íŽë¹íë ê°ì [3-1] 목찚ìì ììë¡ ì ìí ê° ì°ì°ìì ì°ì ìì ê°ìŽë€. ìŽë¡ìš í¹ì í í°ì ëí ì°ì ìì ê°ì ë§€ê²šë Œ ì ìŽë€. ì륌 ë€ìŽ, == ì != í í°ì ê°ì ì°ì ìììŽê³ , > ì < ê° ê°ì ì°ì ìì, + ì -ê°, * ì /ê° ìë¡ ê°ì ì°ì ìììŽë€.
ê·žëŠ¬ê³ ì°ì°ì ì°ì ìì êŽë šíì¬ ì ížì± íšì륌 2ê°ì§ë¥Œ ì ìí ê²ìŽë€. ìŽë íìê° ë°ëŒë³Žê³ ìë í í°, ê·žëŠ¬ê³ ê·ž ë€ìì í í°ì 맀íëë ì°ì°ì ì°ì ìì륌 ë°ííë íšììŽë€.
// parser/parser.go
func (p *Parser) peekPrecedence() int {
if p, ok := precedences[p.peekToken.Type]; ok {
return p
}
return LOWEST
}
func (p *Parser) curPrecedence() int {
if p, ok := precedences[p.curToken.Type]; ok {
return p
}
return LOWEST
}
ì ìì€ìœëì ë¡ì§ì ë§€ì° ê°ëšíë ì€ëª ì ë°ë¡ íì§ ìê² ë€. ê·ž ë€ìì íì륌 ìŽêž°íí ë ê° ì€ì ì°ì°ì í í°ì ë§ë íì± íšì륌 ë±ë¡íì.
// parser/parser.go
func New(l *lexer.Lexer) *Parser {
(... ìëµ ...)
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression)
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
// ì€ì ì°ì°ìë€ì ëí íì± íšì ì¶ê°
p.infixParseFns = make(map[token.TokenType]infixParseFn)
p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.PLUS, p.parseInfixExpression)
p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.SLASH, p.parseInfixExpression)
p.registerInfix(token.ASTERISK, p.parseInfixExpression)
p.registerInfix(token.EQ, p.parseInfixExpression)
p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
p.registerInfix(token.LT, p.parseInfixExpression)
p.registerInfix(token.GT, p.parseInfixExpression)
return p
ìë¡ìŽ ë©ìëê° ë±ì¥íë€. ë°ë¡ parseInfixExpressionìŽë€. ìŽ ë©ìëì ìê¹ì륌 볎ì.
// parser/parser.go
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
expression := &ast.InfixExpression{
Token: p.curToken,
Left: left,
Operator: p.curToken.Literal,
}
precedence := p.curPrecedence()
p.nextToken()
expression.Right = p.parseExpression(precedence)
return expression
}
ìŽì ì ìì±í parsePrefixExpression ë©ìë ìê¹ìì ê±°ì ì ì¬íë€. ì°šìŽì ì precedence ëŒë ë³ììë€ê° íì¬ í í°ì ì°ì°ì ì°ì ìì륌 ë°ííë€. ì¬êž°ììë nextToken íšì륌 ížì¶íŽì íì¬ ìì¹ë¥Œ ë€ì í í°ìŒë¡ ì®êžŽë€. ì¬êž°ìë ìŽ ëìì ì ìíí ê¹?
parseInfixExpression ë©ìë륌 ížì¶íë€ë ê²ì íìê° íì¬ ë°ëŒë³Žë í í°ìŽ ì€ì ì°ì°ì(ex. +, - ..) í í°ìŽëŒë ê²ìŽë€. ê·žë¬ë¯ë¡ nextToken íšì륌 í ë² ížì¶íê² ëë©Ž íì¬ ìì¹ì í í°ìŽ ì€ì ì°ì°ìì ì€ë¥žìªœì ìë ííììŒë¡ ìŽëíê² ëë€. ê·žëŠ¬ê³ ë ë€ parseExpression íšì륌 ížì¶íŽì InfixExpression 구조첎ì Right ë©€ë²ì í ë¹íŽì€ë€.
ìì§ ëë ê²ì ìëë€. ì€ì ì°ì°ì ííì íì± êž°ë¥ìŽ ì¶ê°ëšì ë°ëŒ ìê¹ ììì ì ìí parseExpression ë©ìëì ê°ì ìŽ íìíë€. íì¬ parseExpression ë©ìëì ìê¹ìë ë€ì곌 ê°ë€.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
return leftExp
}
íì¬ë ì ì ì°ì°ì ííìë§ ì²ëŠ¬í ì ìëë¡ ëìŽ ìë ìíìŽë©° precedence ëŒë ìžì ìŠ, ì°ì°ì ì°ì ìì ê°ì íì©íì§ë ìë ìíìŽë€. ì€ì ì°ì°ì ííìë ì²ëŠ¬í ì ìëë¡ ê°ì í parseExpression ë©ìëë ë€ì곌 ê°ë€.
// parser/parser.go
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
leftExp := prefix()
for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
infix := p.infixParseFns[p.peekToken.Type]
if infix == nil {
return leftExp
}
p.nextToken()
leftExp = infix(leftExp)
}
return leftExp
}
ì°ì°ì ì°ì ìì륌 ì¬ì©íŽì for loop륌 ëëë°, 구첎ì ìŒë¡ ìŽë»ê² ëìíëì§ë ë€ì í¬ì€í ìì íë« íìì ëì ì늬륌 ìŽíŽë³Žë©Žì ìŽíŽíŽë³Žì. ê¶êžíë€ë©Ž ì§êžê¹ì§ ìì±í ìì€ìœëì ëí ìží ììë¡ "2 == 2;" 륌 ë£ëë€ê³ ê°ì íê³ ìœëì íëŠì ë°ëŒê°ë³Žì.