๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Computer Science

[CS] ๋‚˜๋งŒ์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! (2): Parser ๋งŒ๋“ค๊ธฐ - ์„ธ๋ฒˆ์งธ

๋ฐ˜์‘ํ˜•

๐Ÿ”Š ํ•ด๋‹น ํฌ์ŠคํŒ…์€ ๋ฐ‘๋ฐ”๋‹ฅ๋ถ€ํ„ฐ ๋งŒ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ in go ์ฑ…์„ ์ฝ๊ณ  ๊ฐœ์ธ์ ์ธ ์ •๋ฆฌ ๋ชฉ์  ํ•˜์— ์ž‘์„ฑ๋œ ๊ธ€์ž…๋‹ˆ๋‹ค. ๋ณธ ํฌ์ŠคํŒ…์— ์‚ฌ์šฉ๋œ ์ž๋ฃŒ๋Š” ๋ชจ๋‘ ๋ณธ์ธ์ด ์ง์ ‘ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Œ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
 

์ถœ์ฒ˜: Yes24


์ง์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” ์šฐ๋ฆฌ๋งŒ์˜ ํŒŒ์„œ์— ๋‹ค์–‘ํ•œ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํƒ‘์žฌํ•ด๋ณด์•˜๋‹ค. ๋‹ค๋งŒ, ๋งˆ์ง€๋ง‰์— ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋๋ƒˆ๋Š”๋ฐ, ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋„์‹ํ™”๋ฅผ ํ•ด๋ณด๋ฉด์„œ ์†Œ์Šค์ฝ”๋“œ๋ž‘ ๋งคํ•‘ํ•œ ํ›„, ๊ตฌ์ฒด์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•œ ํ”„๋žซํŒŒ์„œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋” ๋‚˜์•„๊ฐ€ ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ํ‘œํ˜„์‹๋„ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

1. ํ”„๋žซ(Pratt) ํŒŒ์„œ์˜ ๋™์ž‘ ์›๋ฆฌ

๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋˜ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๋™์ž‘์„ ์‚ดํŽด๋ณด๋ฉด์„œ ํ”„๋žซ ํŒŒ์„œ์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•ด๋ณด์ž. ํŒŒ์‹ฑํ•  ์˜ˆ์ œ ์†Œ์Šค์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

1 + 2 + 3;

 

ํ˜„์žฌ ์†Œ์Šค์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” Go์–ธ์–ด ์†Œ์Šค์ฝ”๋“œ์— ์ž‘์„ฑ๋œ ๊ตฌ์กฐ์ฒด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ–ˆ์„ ๋•Œ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ์–ป์„ ์ตœ์ข… AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋„์‹ํ™” ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

1 + 2 + 3; ๋ช…๋ น๋ฌธ์„ AST ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋„์‹ํ™” ํ•œ๋‹ค๋ฉด?

 

๊ฒฐ๋ก ์„ ์‚ดํŽด๋ณด์•˜์œผ๋‹ˆ, ์ด์ œ ์ด ๊ฒฐ๋ก ์„ ๋„์ถœํ•˜๋Š” ๊ณผ์ •์˜ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๋ณด์ž. Go ์†Œ์Šค์ฝ”๋“œ์—์„œ๋Š” ์œ„ AST ์ž๋ฃŒ๊ตฌ์กฐ์˜ ๋” ๊นŠ์€ ๋ށ์Šค๋ถ€ํ„ฐ ์ฐจ๋ก€๋Œ€๋กœ ๊ตฌํ˜„๋œ๋‹ค.

 

๊ฐ€์žฅ ๋จผ์ € ์œ„ ์ž…๋ ฅ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ถ€๋ถ„์ด๋‹ค.

 

// parser/parser_test.go

func TestParseInfixExpression(t *testing.T) {
	input := "1 + 2 + 3;"

	l := lexer.New(input)
	p := New(l)
	program := p.ParseProgram()
	checkParserErrors(t, p)
    
    (... ์ƒ๋žต ...)

 

๋จผ์ € ๋ ‰์„œ์™€ ํŒŒ์„œ๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค. ์†Œ์Šค์ฝ”๋“œ๋กœ ์น˜๋ฉด l := lexer.New(input) ์™€ p := New(l) ๋ถ€๋ถ„์ด๋‹ค. ์ด ๋ถ€๋ถ„์ด ์‹คํ–‰๋จ์œผ๋กœ์จ ํŒŒ์„œ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ† ํฐ ์œ„์น˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

์ด์ œ ๋ ‰์‹ฑ๊ณผ ํŒŒ์‹ฑ์ด ์‹ค์งˆ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ ์ง€์ ์€ ๋ฐ”๋กœ p.ParseProgram() ๋ฉ”์†Œ๋“œ๋‹ค. ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ณด์ž.

 

// parser/parser.go

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		p.nextToken()
	}
	return program
}

 

p.curToken์ด EOF ํ† ํฐ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— for-loop์— ์ง„์ž…ํ•˜๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ loop๋ฅผ ๋Œ๋ฉด์„œ p.parseStatement ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด์ œ ๊ทธ๋Ÿฌ๋ฉด parseStatement ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž.

 

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

 

ํ˜„์žฌ ํ† ํฐ์˜ ํƒ€์ž…์— ๋”ฐ๋ผ switch-case ๊ตฌ๋ฌธ์„ ํƒ€๋ฉด์„œ ์–ด๋–ค ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ• ์ง€ ๊ฒฐ์ •๋œ๋‹ค. ํ˜„์žฌ ํ† ํฐ์€ let ๋ฌธ๋„, return ๋ฌธ๋„ ์•„๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ default ๊ตฌ๋ฌธ์„ ํƒ€๊ฒŒ ๋˜์–ด parseExpressionStatement ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด์ œ parseExpressionStatement ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž.

 

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

 

ํ‘œํ˜„์‹ ๋…ธ๋“œ๋ฅผ ์˜๋ฏธํ•˜๋Š” ExpressionStatement ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ์ด ๋•Œ ๊ตฌ์กฐ์ฒด์˜ ๋ฉค๋ฒ„์—๋Š” ํ˜„์žฌ ํ† ํฐ์ธ 1์„ ๋‹ด๋Š”๋‹ค. ์ด 1 ์ด๋ผ๋Š” ํ† ํฐ์˜ ํƒ€์ž…์€ ์‹๋ณ„์ž๋ฅผ ์˜๋ฏธํ•˜๋Š” IDENT ํƒ€์ž…์ด ๋‹ด๊ฒจ์ ธ ์žˆ๋‹ค.(์ด IDENT ํ† ํฐ์ด ๋‹ด๊ฒจ์ง€๋Š” ๋™์ž‘์ด ์‹ค์ œ ์ˆ˜ํ–‰๋˜๋Š” ํƒ€์ด๋ฐ์€ ์œ„์—์„œ ๋ ‰์„œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ์ด๋‹ค. ์†Œ์Šค์ฝ”๋“œ์—์„œ ๋ ‰์„œ์˜ NextToken ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž!) ์ด์ œ ํ•ด๋‹น ๊ตฌ์กฐ์ฒด์˜ Expression ๋ฉค๋ฒ„์—๋Š” ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ํ• ๋‹นํ•˜๋Š”๋ฐ, ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์€ ๋ฐ”๋กœ parseExpression ๋ฉ”์†Œ๋“œ๋‹ค. ์ฐธ๊ณ ๋กœ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์— LOWEST ๋ผ๋Š” ๊ฐ€์žฅ ๋‚ฎ์€ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„์ธ 0์„ ์ธ์ž๋กœ ๋„˜๊ฒจ์ค€๋‹ค. ์ด์ œ 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
}

 

๊ฐ€์žฅ ๋จผ์ € p.curToken์— ๋งคํ•‘๋˜๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š”์ง€ ์ฒดํฌํ•œ๋‹ค. p.curToken์€ ์—ฌ์ „ํžˆ 1์ด๋ผ๋Š” IDENT(์‹๋ณ„์ž) ํƒ€์ž…์˜ ํ† ํฐ์ด๋‹ค. ์ด ํ† ํฐ์— ๋งž๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜๋Š” ์กด์žฌํ• ๊นŒ? ์กด์žฌํ•˜๋Š”์ง€ ๋ณด๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ€์žฅ ์ฒ˜์Œ์— ํŒŒ์„œ๋ฅผ ์ดˆ๊ธฐํ™”ํ–ˆ๋˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์•„์•ผ ํ•œ๋‹ค.

 

// 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)     // IDENT ํ† ํฐ์— ๋Œ€ํ•œ ํŒŒ์‹ฑํ•จ์ˆ˜!
	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
}

 

์ฃผ์„ ์ฒ˜๋ฆฌ๋กœ๋„ ํ•ด๋†“์•˜๋‹ค. IDENT ํ† ํฐ์— ๋งคํ•‘๋˜๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜์ธ parseIdentifier ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋“ฑ๋กํ•ด๋†“์•˜๋˜ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์‹œ parseExpression ๋ฉ”์†Œ๋“œ ์†Œ์Šค์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ, IDENT ํ† ํฐ์— ๋งคํ•‘๋˜๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜์ธ parseIdentifier๋ฅผ ์–ป๊ฒŒ๋˜๊ณ , ์ด parseIdentifier ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ left ๋ผ๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ด์ œ parseIdentifier ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณผ ์ฐจ๋ก€๋‹ค.

 

// parser/parser.go

func (p *Parser) parseIdentifier() ast.Expression {
	return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}

 

๋กœ์ง์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ๋‹จ์ˆœํžˆ curToken์˜ ํ† ํฐ์ธ IDENT์™€ ํ† ํฐ์˜ ๊ฐ’์ธ 1์„ ์‹๋ณ„์ž ๊ตฌ์กฐ์ฒด์˜ ๋ฉค๋ฒ„๋กœ ํ• ๋‹นํ•œ ํ›„ ์‹๋ณ„์ž ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์‹œ 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()

	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
}

 

๋ฐฉ๊ธˆ๊นŒ์ง€ํ•ด์„œ leftExp := prefix() ๋ผ๋Š” ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜์—ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ for-loop์— ์ง„์ž…ํ•  ์ฐจ๋ก€๋‹ค. peekTokenIs ํ•จ์ˆ˜๋Š” ์˜ˆ์ „์— ์‚ดํŽด๋ณธ ๋ฉ”์†Œ๋“œ์ธ๋ฐ, ์ด๋ฆ„์—๋„ ์•Œ ์ˆ˜ ์žˆ๋‹ค์‹œํ”ผ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋‹ค์Œ ์œ„์น˜ ํ† ํฐ์ธ p.peekToken์ด SEMICOLON ํ† ํฐ์ธ์ง€ ํ™•์ธํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์•ž์— not ์—ฐ์‚ฐ์ž(!)๊ฐ€ ๋ถ™์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ ๋‹ค์Œ ์œ„์น˜ ํ† ํฐ์ด SEMICOLON์ด ์•„๋‹ ๊ฒฝ์šฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. 

 

๊ทธ๋ฆฌ๊ณ  ์ด์ œ ๋“œ๋””์–ด ์ธ์ž๋กœ ๋ฐ›์€ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„์ธ precedence๋ฅผ ํ™œ์šฉํ•  ์ฐจ๋ก€๋‹ค. ํ˜„์žฌ precedence์—๋Š” LOWEST ๊ฐ’์ด ๋“ค์–ด์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  peekPrecedence ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ํ˜„์žฌ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋‹ค์Œ ์œ„์น˜ ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๊ณ , ์ด 2๊ฐœ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์„ ๋น„๊ตํ•œ๋’ค ๋งŒ์•ฝ ๋‹ค์Œ ์œ„์น˜ ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๊ฐ€ ํฌ๋‹ค๋ฉด for-loop ๊ตฌ๋ฌธ์„ ํƒ€๋„๋ก ํ•œ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ ์œ„์น˜ ํ† ํฐ์€ ๋ฌด์—‡์ผ๊นŒ? ์—ฌ์ „ํžˆ ํ˜„์žฌ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ† ํฐ์˜ ์œ„์น˜ ์ƒํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

์ฆ‰, ๋‹ค์Œ ์œ„์น˜์˜ ํ† ํฐ์ธ peekToken๋Š” ์—ฐ์‚ฐ์ž ํ† ํฐ +(PLUS)์„ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด์ œ ์ด peekToken์— ๋งคํ•‘๋œ ์ค‘์œ„ ์—ฐ์‚ฐ์ž ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ฐพ์•„๋ณด์ž. PLUS ํ† ํฐ์— ๋งž๋Š” ํŒŒ์‹ฑ ๋ฉ”์†Œ๋“œ๋Š” parseInfixExpression ์ด๋‹ค. parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ด์ „์— ๋จผ์ € nextToken() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•จ์— ๋”ฐ๋ผ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ† ํฐ์˜ ์œ„์น˜๊ฐ€ 1์นธ์”ฉ ๋‹ค์Œ์œผ๋กœ ์˜ฎ๊ฒจ์ง„๋‹ค.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

ํ† ํฐ์˜ ์œ„์น˜๋ฅผ ์˜ฎ๊ธด ํ›„ parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์ œ ํ˜ธ์ถœํ•  ์ฐจ๋ก€๋‹ค. 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
}

 

parseInfixExpression ๋ฉ”์†Œ๋“œ๋Š” ํ‘œํ˜„์‹ ํ•˜๋‚˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค. ์ธ์ž ์ด๋ฆ„์ด left ์ธ ๊ฒƒ์—์„œ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋“ฏ์ด, left๋Š” ์ค‘์œ„ ์—ฐ์‚ฐ์ž๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์™ผ์ชฝ์— ์žˆ๋Š” ํ‘œํ˜„์‹์„ ์˜๋ฏธํ•œ๋‹ค. ํ˜„์žฌ ์ƒํ™ฉ์—์„œ left ์ธ์ž์— ๋‹ด๊ฒจ ์žˆ๋Š” ํ‘œํ˜„์‹์€ ์•„๊นŒ ์œ„์—์„œ ์‚ดํŽด๋ณธ 1 ์ด๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์œผ๋กœ ๋งŒ๋“  ํ‘œํ˜„์‹ ๋…ธ๋“œ์ด๋‹ค. ์ด ๋…ธ๋“œ๋ฅผ InfixExpression ๊ตฌ์กฐ์ฒด์˜ Left ๋ฉค๋ฒ„์— ํ• ๋‹นํ•œ๋‹ค. ์ด ์ˆœ๊ฐ„์„ AST ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋„์‹ํ™” ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํƒœ์ด๋‹ค.

 

 

๋‹ค์‹œ parseInfixExpression ๋ฉ”์†Œ๋“œ ์†Œ์Šค์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋ณด์ž. ์ด์ œ precedence := p.curPrecedence() ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด์ž. curPrecedence ๋ฉ”์†Œ๋“œ๋Š” ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„ํƒœ ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๋ฅผ ์–ป๊ฒŒ ๋œ๋‹ค. ํ˜„์žฌ ํ† ํฐ์€ +(PLUS) ํ† ํฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์€ 4์ด๋‹ค.(๊ฐ ํ† ํฐ์— ๋งคํ•‘๋œ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ์ƒ์ˆ˜ ๊ฐ’์€ parser/parser.go ์†Œ์Šค์ฝ”๋“œ์˜ ๋ณ€์ˆ˜๋กœ ์ •์˜๋˜์–ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜์ž) ์ฐธ๊ณ ๋กœ, ์ด ๋•Œ ๋˜ ํ•œ๋ฒˆ p.nextToken() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ† ํฐ์˜ ์œ„์น˜๋ฅผ ํ•œ ๋ฒˆ ๋” 1์นธ์”ฉ ์ด๋™์‹œํ‚จ๋‹ค. 

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

๊ทธ๋ฆฌ๊ณ  ์ด 4๋ผ๋Š” ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์„ parseExpression ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋กœ ๋„ฃ์–ด์„œ ๋‹ค์‹œ ํ•œ๋ฒˆ ์žฌ๊ท€์ ์œผ๋กœ 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()

	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
}

 

curToken์€ ํ˜„์žฌ 2๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์œผ๋กœ ์œ„์น˜๊ฐ€ ์˜ฎ๊ฒจ์ง„ ์ƒํƒœ์ด๋‹ค. ์ด ํ† ํฐ์— ๋งคํ•‘๋˜์–ด ์žˆ๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž. ์ด ๋˜ํ•œ ์‹๋ณ„์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์—์„œ 1 ์ด๋ผ๋Š” ํ† ํฐ์— ๋Œ€ํ•ด ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋ž‘ ๋˜‘๊ฐ™๋‹ค. ์ด๋ฒˆ์—๋Š” 2๋ผ๋Š” ์‹๋ณ„์ž ํ‘œํ˜„์‹์— ๋Œ€ํ•ด ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด๋Š” ๋ถ€๋ถ„์€ ์ค‘๋ณต๋˜๋‹ˆ ์ƒ๋žตํ•˜๊ฒ ๋‹ค. ๋ฐ”๋กœ for-loop ๊ตฌ๋ฌธ์œผ๋กœ ๊ฐ€๋ณด์ž. 

 

peekToken์€ +(PLUS)์ธ ์ƒํƒœ์ด๋‹ค. ์ด ํ† ํฐ์€ SEMICOLON ํ† ํฐ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— for-loop๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ์œ„ํ•œ ์ฒซ ๋ฒˆ์งธ ์กฐ๊ฑด์€ ๋งŒ์กฑํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋‘๋ฒˆ์งธ ์กฐ๊ฑด์€ ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ฆ‰, ํ˜„์žฌ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›์€ precedence ๊ฐ’์€ 4์ธ ์ƒํƒœ์ด๋‹ค. peekPrecedence ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์–ป๋Š” ๊ฐ’๋„ 4์ด๋‹ค. ์ด ๋‘ ๊ฐœ์˜ ๊ฐ’์€ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— for-loop ๊ตฌ๋ฌธ์œผ๋กœ ๋“ค์–ด๊ฐ€์ง€ ์•Š๊ณ , ๊ณง๋ฐ”๋กœ return ๊ตฌ๋ฌธ์„ ๋งŒ๋‚˜์„œ leftExp๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ด leftExp ๋ณ€์ˆ˜์—๋Š” 2๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์„ ํŒŒ์‹ฑํ•œ ํ‘œํ˜„์‹์ด ๋‹ด๊ฒจ ์žˆ๋Š” ์ƒํƒœ์ด๋‹ค.

 

parseExpression ๋ฉ”์†Œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์—ˆ์œผ๋‹ˆ ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ํ˜ธ์ถœ์ž ๋ฉ”์†Œ๋“œ์ธ 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
}

 

expression ์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด์˜ Right ๋ฉค๋ฒ„์— ๋ฐฉ๊ธˆ parseExpression ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•œ leftExp ๋ณ€์ˆ˜ ์ฆ‰, 2๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์„ ํŒŒ์‹ฑํ•œ ํ‘œํ˜„์‹ ๋…ธ๋“œ๋ฅผ ํ• ๋‹นํ•œ๋‹ค. ๊ฒฐ๊ตญ, ์ด๋ ‡๊ฒŒ ๋˜๋ฉด์„œ AST ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.

 

 

์ด์ œ parseInfixExpression ๋ฉ”์†Œ๋“œ๋„ ๋ชจ๋‘ ๋์ด ๋‚ฌ๋‹ค. ํ—ท๊ฐˆ๋ฆฌ๊ฒ ์ง€๋งŒ.. ์ด parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ํ˜ธ์ถœ์ž๋Š” ๋‹ค๋ฅธ 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()

	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
}

 

์ด๋ฒˆ parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋‹น์‹œ์— ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ์—ˆ๋˜ precednce ๊ฐ’์€ LOWEST ์˜€๋˜ ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๋Š”๊ฐ€? ์ž˜ ํŒ๋‹จ์ด ์•ˆ๋œ๋‹ค๋ฉด ๋‹ค์‹œ ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€์„œ ์ฒœ์ฒœํžˆ ํ๋ฆ„์„ ๋”ฐ๋ผ์™€๋ณด์ž. ๋‹จ ๋ฒˆ์— ์ดํ•ดํ•˜์ง€ ๋ชปํ•ด๋„ ๊ดœ์ฐฎ๋‹ค! ํ•„์ž๋„ ๋ช‡ ๋ฒˆ์”ฉ์ด๋‚˜ ๋‹ค์‹œ ํ™•์ธํ–ˆ๋‹ค!

 

์ด์ œ 2๋ฒˆ์จฐ for-loop๋ฅผ ๋Œ ์ฐจ๋ก€๋‹ค. ๊ทธ ์ „์— ํ˜„์žฌ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ํ† ํฐ๊ณผ ๋‹ค์Œ ํ† ํฐ์ด ๋ฌด์—‡์ธ์ง€ ๋ฆฌ๋งˆ์ธ๋“œ ํ•ด๋ณด์ž.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

peekToken์€ ์—ฌ์ „ํžˆ ์„ธ๋ฏธ์ฝœ๋ก ์ด ์•„๋‹Œ ์ƒํƒœ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  precedence ๊ฐ’์€ ์—ฌ์ „ํžˆ 0์ด๊ณ , peekPrecedence ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์–ป๋Š” ๊ฐ’ ์ฆ‰, peekToken์ธ +(PLUS)์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์€ 4์ด๋‹ค. ์—ฌ์ „ํžˆ 0์€ 4๋ณด๋‹ค ์ž‘๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒˆ์—๋„ for-loop๋ฅผ ํƒ€๋Š” ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•œ๋‹ค. 

 

๋˜ ๋‹ค์‹œ +(PLUS) ํ† ํฐ์— ๋งคํ•‘๋œ ์ค‘์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜์ธ parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ์–ป๋Š”๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ง์ „ nextToken ๋จผ์ € ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ํ† ํฐ์˜ ์œ„์น˜๊ฐ€ 1์นธ์”ฉ ๋˜ ์ด๋™ํ•œ๋‹ค.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

๊ทธ๋ฆฌ๊ณ  ์ด์ œ parseInfixExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 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
}

 

left ์ธ์ž์— ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ํ‘œํ˜„์‹ ๋…ธ๋“œ๋Š” 2๋ผ๋Š” ํ† ํฐ์„ ํŒŒ์‹ฑํ•œ ์‹๋ณ„์ž ํ‘œํ˜„์‹ ๋…ธ๋“œ์ธ ์ƒํƒœ์ด๋‹ค. ์ด ๋…ธ๋“œ๋ฅผ InfixExpression ๊ตฌ์กฐ์ฒด์˜ Left ๋ฉค๋ฒ„์— ํ• ๋‹นํ•œ๋‹ค. ์ด ์ˆœ๊ฐ„์— ๋งŒ๋“ค์–ด์ง€๋Š” AST ์ž๋ฃŒ๊ตฌ์กฐ ์ƒํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

 

๊ทธ๋ฆฌ๊ณ  ํ˜„์žฌ ํ† ํฐ์ธ curToken์€ +(PLUS)๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ƒํƒœ์ด๊ณ  ์ด ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž์˜ ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์€ 4์˜€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•œ๋ฒˆ nextToken ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ† ํฐ์˜ ์œ„์น˜๋ฅผ ํ•œ ์นธ์”ฉ ์˜ฎ๊ธด๋‹ค.

 

curToken์€ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ˜„์žฌ ์œ„์น˜, peekToken์€ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค

 

๊ทธ๋ฆฌ๊ณ  parseExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ +(PLUS) ํ† ํฐ์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„ ๊ฐ’์ธ 4๋ฅผ ์ธ์ž๋กœ ๋„ฃ์–ด์ค€๋‹ค. 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
}

 

curToken์€ ํ˜„์žฌ 3์ด๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์ด๋‹ค. ์ด ์‹๋ณ„์ž ํ† ํฐ์— ๋งคํ•‘๋˜๋Š” ์ „์œ„ ์—ฐ์‚ฐ์ž ํŒŒ์‹ฑ ํ•จ์ˆ˜์ธ parseIdentifer ๋ฉ”์†Œ๋“œ๋ฅผ ์–ป๊ณ  ํ˜ธ์ถœํ•ด์„œ ์‹๋ณ„์ž ํ‘œํ˜„์‹ ๋…ธ๋“œ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๋Š”๋‹ค. ์ด ๋…ธ๋“œ๋Š” leftExp ๋ณ€์ˆ˜์— ํ• ๋‹น๋˜์—ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  for-loop์— ๋˜ ์ง„์ž…ํ•œ๋‹ค. ์ด ๋•Œ๋Š” peekToken์ด SEMICOLON์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋‹จ ์ด ์กฐ๊ฑด๋ถ€ํ„ฐ false ์ด๊ธฐ ๋•Œ๋ฌธ์— for-loop๋ฅผ ํƒ€์ง€ ์•Š๋Š”๋‹ค. ๋ฌผ๋ก  ์ด ์„ธ๋ฏธ์ฝœ๋ก  ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•œ๋‹ค๊ณ  ํ•ด๋„ precedence < p.peekPrecedence() ์กฐ๊ฑด์€ ๋ถ€ํ•ฉํ•˜์ง€ ๋ชปํ•œ๋‹ค. peekPrecedence ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ peekToken์ธ SEMICOLON์˜ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๋Š” ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— LOWEST์ธ 0์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๋ฉด์„œ ํ˜„์žฌ precedence ๊ฐ’์€ 4์ด์ง€๋งŒ, peekPrecedence ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ 0์œผ๋กœ ๋Œ€์†Œ ๋น„๊ต ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๊ณง๋ฐ”๋กœ 3์ด๋ผ๋Š” ์‹๋ณ„์ž ํ† ํฐ์„ ํŒŒ์‹ฑํ•œ ์‹๋ณ„์ž ํ‘œํ˜„์‹ ๋…ธ๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

์ด 3์ด๋ผ๋Š” ์‹๋ณ„์ž ํ‘œํ˜„์‹ ๋…ธ๋“œ๋Š” parseInfixExpression ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ expression ๊ตฌ์กฐ์ฒด์˜ Right ๋ฉค๋ฒ„์— ํ• ๋‹น๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•ด์„œ ์ตœ์ข… ์ ์ธ AST ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค์–ด์ง„๋‹ค.

 

2. Bool ๋ฆฌํ„ฐ๋Ÿด ํŒŒ์‹ฑํ•˜๊ธฐ

์ด์ œ ํ”„๋žซ ํŒŒ์„œ์˜ ๋™์ž‘ ์›๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์œผ๋‹ˆ, ์šฐ๋ฆฌ์˜ ํŒŒ์„œ๊ฐ€ ํŒŒ์‹ฑํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ํ‘œํ˜„์‹์„ ๋” ์ถ”๊ฐ€ํ•ด๋ณด๋„๋ก ํ•˜์ž. ๊ฐ€์žฅ ๋จผ์ € Boolean์„ ์˜๋ฏธํ•˜๋Š” Bool ๋ฆฌํ„ฐ๋Ÿด์ด๋‹ค. Monkey ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ Bool ๋ฆฌํ„ฐ๋Ÿด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์†Œ์Šค์ฝ”๋“œ๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ๋‹ค.

 

true;
false;
let foobar = true;
let barfoo = false;

 

๊ฐ€์žฅ ๋จผ์ € ํ•  ์ž‘์—…์€ Bool ๋ฆฌํ„ฐ๋Ÿด์„ ๋‹ด์„ AST ๋…ธ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

// ast/ast.go

type Boolean struct {
	Token token.Token
	Value bool
}

func (b *Boolean) expressionNode()      {}
func (b *Boolean) TokenLiteral() string { return b.Token.Literal }
func (b *Boolean) String() string       { return b.Token.Literal }

 

๊ทธ๋ฆฌ๊ณ  ์ด Bool ๋ฆฌํ„ฐ๋Ÿด์„ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์ „์šฉ ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ map ์ž๋ฃŒ๊ตฌ์กฐ์— ๋“ฑ๋กํ•˜์ž. 

 

// parser/parser.go

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}
	(... ์ƒ๋žต ...)
	p.registerPrefix(token.TRUE, p.parseBoolean)
	p.registerPrefix(token.FALSE, p.parseBoolean)

	(... ์ƒ๋žต ...)
	return p
}

 

์•„์ง Bool ๋ฆฌํ„ฐ๋Ÿด์„ ์‹ค์งˆ์ ์œผ๋กœ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜ ์ฆ‰, parseBoolean ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณธ ์ ์ด ์—†๋‹ค. ์ด์ œ parseBoolean ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž. 

 

// parser/parser.go

func (p *Parser) parseBoolean() ast.Expression {
	return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
}

 

curTokenIs ๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ด์ „ ํฌ์ŠคํŒ…์—์„œ ์†Œ๊ฐœํ•œ ํ•จ์ˆ˜๋‹ค. ์–ด๋ ค์šด ๋กœ์ง์€ ์•„๋‹ˆ๊ณ , ํ˜„์žฌ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ† ํฐ์ด ์ธ์ž๋กœ ๋„ฃ์–ด์ค€, ์—ฌ๊ธฐ์„œ๋Š” TRUE ํ† ํฐ๊ณผ ์ผ์น˜ํ•˜๋ฉด true๋ฅผ, ๋ถˆ์ผ์น˜ํ•˜๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•œ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

์ด์ œ ์ด Bool ๋ฆฌํ„ฐ๋Ÿด์„ ์ž˜ ํŒŒ์‹ฑํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. ์ด๋ฒˆ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ ๊ธฐ์กด์— ์ž‘์„ฑํ•ด์™”๋˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๊ทธ ์ฃผ์ธ๊ณต์€ testLiteralExpression ์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.

 

// parser/parser_test.go

func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool {
	switch v := expected.(type) {
	case int:
		return testIntegerLiteral(t, exp, int64(v))
	case int64:
		return testIntegerLiteral(t, exp, v)
	case string:
		return testIdentifier(t, exp, v)
	case bool:
		return testBooleanLiteral(t, exp, v)
	}
	t.Errorf("type of exp not handled. got=%T", exp)
	return false
}

 

์ฒ˜์Œ ๋ณด๋Š” Go lang ๋ฌธ๋ฒ•์ด ๋“ฑ์žฅํ–ˆ๋‹ค. ์šฐ์„  ๋จผ์ € ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ interface{} ๋ผ๊ณ  ๋งค์šฐ ์ผ๋ฐ˜ํ™”๋œ ํƒ€์ž…์˜ ํ˜•์‹์„ ์ •์˜ํ–ˆ๋‹ค. interface๋Š” go-lang์—์„œ ๋ชจ๋“  ํƒ€์ž…์„ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋Š” ์ตœ์ƒ์œ„ ํƒ€์ž…์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ•ด๋‹น ํ˜•์‹์œผ๋กœ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค๋Š” ๊ฒƒ์€ ๊ทธ ์ธ์ž์— ์–ด๋–ค ํƒ€์ž…์ด๋ผ๋„ ์˜ฌ ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ ‡๊ฒŒ interface ํƒ€์ž…์œผ๋กœ ์ „๋‹ฌ๋ฐ›์€ ์ธ์ž๋ฅผ .(type) ์ด๋ผ๋Š” ๋ฌธ๋ฒ•์„ ์ด์šฉํ•ด์„œ v ๋ผ๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค. ์ด .(type) ์ด๋ผ๋Š” ๋ฌธ๋ฒ•์€ ํƒ€์ž… ์Šค์œ„์น˜๋ผ๋Š” ๊ตฌ๋ฌธ์œผ๋กœ, interface ๋ผ๋Š” ๋งค์šฐ ์ผ๋ฐ˜ํ™”๋œ ์•„ํŒ์„ ๊ตฌ์ฒด์ ์ธ ํŠน์ • ํƒ€์ž…์œผ๋กœ ํŒ๋ณ„ํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•œ๋‹ค. ์‹ค์ œ๋กœ ์ € v ๋ผ๋Š” ๋ณ€์ˆ˜์—๋Š” expected ๋ผ๋Š” ๋ณ€์ˆ˜์— ๋‹ด๊ฒจ ์žˆ๋Š” ๊ฐ’์„ ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜์‹œํ‚จ ํ›„์˜ ๊ฐ’์ด ํ• ๋‹น๋˜์–ด ์žˆ๋‹ค.

 

์ด์ œ ์ƒˆ๋กœ์šด testBooleanLiteral ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. ํฌ๊ฒŒ ๋ณ„๊ฑด ์—†๊ณ , testIdentifier ์ด๋‚˜ testIntegerLiteral ๋ฉ”์†Œ๋“œ์ฒ˜๋Ÿผ ํŠน์ • AST ๋…ธ๋“œ์— ๋‹ด๊ฒจ์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ Bool ๋ฆฌํ„ฐ๋Ÿด์ธ์ง€ ํ™•์ธํ•˜๋Š” ์šฉ๋„์ด๋‹ค.

 

// parser/parser_test.go

func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
	bo, ok := exp.(*ast.Boolean)
	if !ok {
		t.Errorf("exp not *ast.Boolean. got=%T", exp)
		return false
	}
	if bo.Value != value {
		t.Errorf("bo.Value not %t. got=%t", value, bo.Value)
		return false 
	}
	if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
		t.Errorf("bo.TokenLiteral not %t. got=%s", value, bo.TokenLiteral())
		return false
	}
	
	return true
}

3. ๊ทธ๋ฃน ํ‘œํ˜„์‹ ํŒŒ์‹ฑํ•˜๊ธฐ

๋‹ค์Œ์œผ๋กœ ํŒŒ์‹ฑํ•ด๋ณผ ๊ฒƒ์€ ๊ทธ๋ฃน ํ‘œํ˜„์‹์ด๋‹ค. ๊ทธ๋ฃน ํ‘œํ˜„์‹์œผ๋กœ ์ธํ•ด์„œ ๊ทธ๋ฃน์ด ๋งŒ๋“ค์–ด์ง€๋ฉด ์—ฐ์‚ฐ ์šฐ์„ ์ˆœ์œ„์— ๋ณ€๋™์ด ์ƒ๊ธฐ๊ณ , ์ด์— ๋”ฐ๋ผ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋˜๋Š” ์ˆœ์„œ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ์‚ฌ์น™ ์—ฐ์‚ฐ ์•ˆ์—์„œ์˜ ๊ทธ๋ฃน ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ๋‹ค.

 

(5 + 5) * 10;

 

์—ฐ์‚ฐ์ž ์ž์ฒด๋งŒ ๋ณด๋ฉด ๊ณฑ์…ˆ์ด ๋ง์…ˆ๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์ง€๋งŒ, ๋ง์…ˆ์ด ๊ทธ๋ฃน ํ‘œํ˜„์‹ ์†Œ๊ด„ํ˜ธ ์•ˆ์— ๊ฐ์‹ธ์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ง์…ˆ์ด ๊ณฑ์…ˆ๋ณด๋‹ค ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ด ๊ทธ๋ฃน ํ‘œํ˜„์‹ ํŒŒ์‹ฑ์„ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ์ฝ”๋“œ๋Š” TestOperatorPrecedenceParsing ์ด๋ผ๋Š” ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜์— ์•„๋ž˜์˜ ์˜ˆ์ œ ์ž…๋ ฅ์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// parser/parset_test.go

func TestOperatorPrecedenceParsing(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		(... ์ƒ๋žต ...)
		{
			"1 + (2 + 3) + 4",
			"((1 + (2 + 3)) + 4)",
		},
		{
			"(5 + 5) * 2",
			"((5 + 5) * 2)",
		},
		{
			"2 / (5 + 5)",
			"(2 / (5 + 5))",
		},
		{
			"(5 + 5) * 2 * (5 + 5)",
			"(((5 + 5) * 2) * (5 + 5))",
		},
		{
			"-(5 + 5)",
			"(-(5 + 5))",
		},
		{
			"!(true == true)",
			"(!(true == true))",
		},
	}

	for _, tt := range tests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		actual := program.String()
		if actual != tt.expected {
			t.Errorf("expected=%q, got=%q", tt.expected, actual)
		}
	}

 

์ด์ œ ๊ทธ๋ฃน ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜๊ณ , ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ƒ๊ฐ๋ณด๋‹ค ์—„์ฒญ ๊ฐ„๋‹จํ•˜๋‹ค. ๋จผ์ € ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜์ž.

 

// parser/parser.go

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}
	(... ์ƒ๋žต ...)
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)

	(... ์ƒ๋žต ...)
	return p
}

 

๊ทธ๋ฆฌ๊ณ  parseGroupedExpression ์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()
	
	exp := p.parseExpression(LOWEST)
	
	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	return exp
}

 

๋จผ์ € ํ˜„์žฌ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ํ† ํฐ์ด ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ผ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ† ํฐ์˜ ์œ„์น˜๋ฅผ ํ•œ ๋ฒˆ ์˜ฎ๊ฒจ์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ์„ ์ˆœ์œ„ ์—ฐ์‚ฐ์ž ๊ฐ’์„ 0 ์ฆ‰, LOWEST๋กœ ์ค€ ๋’ค ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ† ๋ก ํ•œ๋‹ค. ์ด ์ˆœ๊ฐ„ ์žฌ๊ท€์ ์œผ๋กœ ํŒŒ์‹ฑ์ด ํ˜ธ์ถœ๋˜๊ฒŒ ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ์žฌ๊ท€์ ์œผ๋กœ ํŒŒ์‹ฑ ํ˜ธ์ถœ์ด ์™„๋ฃŒ๋œ ๋‹ค์Œ ํŒŒ์„œ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋‹ค์Œ ํ† ํฐ์˜ ์œ„์น˜๊ฐ€ ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ๋ผ๋ฉด ํ•ด๋‹น ํ‘œํ˜„์‹์„ return ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด ๋•Œ ๋ฐ”๋กœ ๊ทธ๋ฃน ํ‘œํ˜„์‹ ํŒŒ์‹ฑ์ด ์™„๋ฃŒ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

4. ์กฐ๊ฑด ํ‘œํ˜„์‹ ํŒŒ์‹ฑํ•˜๊ธฐ

๋‹ค์Œ์€ ์กฐ๊ฑด ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•  ์ฐจ๋ก€๋‹ค. Monkey ์–ธ์–ด์—์„œ๋„ if-else ๊ตฌ๋ฌธ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž. Monkey ์–ธ์–ด์—์„œ์˜ if-else ๊ตฌ๋ฌธ ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

if (x > y) {
  return x;
} else {
  return y;
}

 

๋ฌผ๋ก  ์œ„์ฒ˜๋Ÿผ else ๊ตฌ๋ฌธ๊นŒ์ง€ ํ™œ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, else๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค. ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

if (x > y) {
  return x;
}

 

if-else ์กฐ๊ฑด์‹์€ ํ‘œํ˜„์‹์ด๋‹ค. ์ด ๋ง์€ if-else ๊ตฌ๋ฌธ์—์„œ ๊ฐ’์„ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค๋Š” ๋œป์ด๋‹ค. ์œ„ ์†Œ์Šค์ฝ”๋“œ๋„ x ์™€ y ๋ผ๋Š” ๊ฐ’์„ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ if-else ์กฐ๊ฑด์‹์ด ๊ฐ–๋Š” ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

if (<CONDITION>) <CONSEQUENCE> else <ALTENATIVE>

 

์œ„ ๊ตฌ์กฐ ์ค‘์—์„œ <CONSEQUNECE> ์™€ <ALTENATIVE>๋Š” ์ค‘๊ด„ํ˜ธ๋กœ ํฌํ˜„๋˜๋Š” ๋ธ”๋ก๋ฌธ์„ ๊ฐ–๋Š”๋‹ค. ์ด๋ฅธ๋ฐ” body ๋ธ”๋ก์„ ๊ฐ–๋Š”๋‹ค. ์ด์ œ if-else ์กฐ๊ฑด์‹์˜ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด์•˜์œผ๋‹ˆ ํŒŒ์‹ฑ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์šฐ๋ฆฌ๋งŒ์˜ ํŒŒ์„œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž. 

 

๊ฐ€์žฅ ๋จผ์ € if-else ์กฐ๊ฑด ํ‘œํ˜„์‹์„ ๋‚˜ํƒ€๋‚ด๋Š” AST ๋…ธ๋“œ๋ฅผ ์ •์˜ํ•˜์ž.

 

// ast/ast.go

type IfExpression struct {
	Token token.Token
	Condition Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

func (ie *IfExpression) expressionNode()      {}
func (ie *IfExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *IfExpression) String() string {
	var out bytes.Buffer
	
	out.WriteString("if")
	out.WriteString(ie.Condition.String())
	out.WriteString(" ")
	out.WriteString(ie.Consequence.String())
	
	if ie.Alternative != nil {
		out.WriteString("else ")
		out.WriteString(ie.Alternative.String())
	}
	return out.String()
}

 

์ƒˆ๋กœ์šด IfExpression ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ํ•ด๋‹น ๊ตฌ์กฐ์ฒด๋Š” 4๊ฐœ์˜ ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๋Š”๋ฐ, ํ•˜๋‚˜๋Š” ๋‹ค๋ฅธ ๊ตฌ์กฐ์ฒด๋“ค๊ณผ ๋™์ผํ•˜๊ฒŒ Token ๊ฐ’์„ ๊ฐ–๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  3๊ฐœ์˜ ๋ฉค๋ฒ„๋Š” ์ง์ „์— ์•Œ์•„๋ณธ if-else ์กฐ๊ฑด ํ‘œํ˜„์‹์˜ ๊ตฌ์กฐ๋กœ ์‚ดํŽด๋ณด์•˜๋˜ 3๊ฐ€์ง€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์กฐ๊ฑด์‹์„ ์˜๋ฏธํ•˜๋Š” Condition์ด๋‹ค. ์ด Condition์€ ํ‘œํ˜„์‹์— ํ•ด๋‹นํ•˜๊ธฐ ๋•Œ๋ฌธ์— Expression ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํƒ€์ž…์œผ๋กœ ๋ช…์‹œํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Consequence์™€ Alternative๋Š” BlockStatement ๋ผ๋Š” ์ฒ˜์Œ๋ณด๋Š” ํƒ€์ž…์˜ ํฌ์ธํ„ฐ ๋ณ€์ˆ˜๋ฅผ ํƒ€์ž…์œผ๋กœ ๋ช…์‹œํ–ˆ๋‹ค. ์ด 2๊ฐœ๋ฅผ ๋ธ”๋ก์„ ๊ฐ–๋Š” ๊ตฌ๋ฌธ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ํƒ€์ž…์„ ๋ช…์‹œํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ด์ œ BlockStatment๊ฐ€ ๋ฌด์Šจ ํƒ€์ž…์ธ์ง€ ๊ตฌ์ฒด์ ์œผ๋กœ ๋ณผ ์ฐจ๋ก€๋‹ค.

 

// ast/ast.go

type BlockStatement struct {
	Token      token.Token
	Statements []Statement
}

func (bs *BlockStatement) statementNode()       {}
func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Literal }
func (bs *BlockStatement) String() string {
	var out bytes.Buffer
	
	for _, s := range bs.Statements {
		out.WriteString(s.String())
	}
	
	return out.String()
}

 

BlockStatment ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋Š” ์Šฌ๋ผ์ด์Šค ํ˜•ํƒœ์˜ ์—ฌ๋Ÿฌ ๊ฐœ์˜ Statment ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” Statments ๋ผ๋Š” ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ด Statment๋Š” 1๊ฐœ์˜ ๋ช…๋ น๋ฌธ์„ ๋œปํ•œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด, BolckStatement๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ช…๋ น๋ฌธ์„ ๊ฐ–๋Š”๋‹ค. ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ๋ฌธ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

if (x > y) {
  z = x + y;
  return z
} else {
  z = x * y;
  return z
}

 

์œ„์ฒ˜๋Ÿผ ํ•˜๋‚˜์˜ ๋ธ”๋ก ์•ˆ์—๋Š” 2๊ฐœ ์ด์ƒ์˜ ๋ช…๋ น๋ฌธ์œผ๋กœ ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ž, ์ด์ œ ์กฐ๊ฑด ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•œ ๋…ธ๋“œ ์ •์˜๋Š” ๋ชจ๋‘ ๋งˆ์ณค๋‹ค. ์ด์ œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

// parser/parser_test.go

func TestIfExpression(t *testing.T) {
	input := `if (x < y) { x }`
	
	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", 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.IfExpression)
	if !ok {
		t.Fatalf("stmt is not ast.IfExpression. got=%T", stmt.Expression)
	}
	
	if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
		return 
	}
	
	if len(exp.Consequence.Statements) != 1 {
		t.Errorf("consequence is not 1 statements. got=%d\n", len(exp.Consequence.Statements))
	}
	
	consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Consequence.Statements[0])
	}
	
	if !testIdentifier(t, consequence.Expression, "x") {
		return 
	}
	
	if exp.Alternative != nil {
		t.Errorf("exp.Alternative.Statements was not nil. got=%+v", exp.Alternative)
	}
    
 func TestIfElseExpression(t *testing.T) {
	input := `if (x < y) { x } else { y }`

	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 %d statements. got=%d\n",
			1, 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.IfExpression)
	if !ok {
		t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression)
	}

	if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
		return
	}

	if len(exp.Consequence.Statements) != 1 {
		t.Errorf("consequence is not 1 statements. got=%d\n",
			len(exp.Consequence.Statements))
	}

	consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
			exp.Consequence.Statements[0])
	}

	if !testIdentifier(t, consequence.Expression, "x") {
		return
	}

	if len(exp.Alternative.Statements) != 1 {
		t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n",
			len(exp.Alternative.Statements))
	}

	alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
			exp.Alternative.Statements[0])
	}

	if !testIdentifier(t, alternative.Expression, "y") {
		return
	}
}

 

ํฌ๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ตฌ์กฐ๊ฐ€ ๋ณ€ํ•œ ๊ฒƒ์€ ์—†๋‹ค. ๋‹จ์ง€ ๋ฐฉ๊ธˆ ์šฐ๋ฆฌ๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์ •์˜ํ•œ if-else ์กฐ๊ฑด์‹ AST ๋…ธ๋“œ๋ฅผ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ๊ตฌ์กฐ์ฒด์˜ ์–ด๋–ค ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•ด์„œ ๊ฒ€์ฆํ•˜๋Š”์ง€ ์ •๋„๋งŒ ๋ฐ”๋€Œ์—ˆ๋‹ค.

 

์ด์ œ ๋‹ค์Œ์œผ๋กœ ํ•  ์ž‘์—…์€ ์‹ค์งˆ์ ์ธ ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•  ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

// parer/parser.go

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}
	// ํ˜„์žฌ ์ดˆ๊ธฐ token ๊ฐ’์€ ๋นˆ ๋ฌธ์ž์ด๊ธฐ ๋•Œ๋ฌธ์—, curToken, peekToken์— ํ† ํฐ์„ ๋‹ด์œผ๋ ค๋ฉด 2๋ฒˆ ์ˆ˜ํ–‰
	p.nextToken()
	p.nextToken()
     
    (... ์ƒ๋žต ...)
	p.registerPrefix(token.IF, p.parseIfExpression)
    
    (... ์ƒ๋žต ...)
    return p

 

if-else ์กฐ๊ฑด ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜์ธ parseIfExpression ๋ฉ”์„œ๋“œ๋ฅผ ๋“ฑ๋กํ–ˆ๋‹ค. ์ด ๋•Œ, ์ „์œ„ํ‘œํ˜„์‹์— ๋“ฑ๋กํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด์œ ๋Š” if-else ์กฐ๊ฑด ํ‘œํ˜„์‹์€ if ๋ผ๋Š” ํ‚ค์›Œ๋“œ๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ๋“ฑ์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด์ œ parseIfExpression ๋ฉ”์†Œ๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ๋ณผ ์ฐจ๋ก€๋‹ค.

 

// parser/parser.go

func (p *Parser) parseIfExpression() ast.Expression {
	expression := &ast.IfExpression{Token: p.curToken}
	
	if !p.expectPeek(token.LPAREN) {
		return nil
	}
	
	p.nextToken()
	expression.Condition = p.parseExpression(LOWEST)
	
	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	
	if !p.expectPeek(token.LBRACE) {
		return nil
	}
	
	expression.Consequence = p.parseBlockStatement()
	
	if p.peekTokenIs(token.ELSE) {
		p.nextToken()
		
		if !p.expectPeek(token.LBRACE) {
			return nil
		}
		
		expression.Alternative = p.parseBlockStatement()
	}
	
	return expression
}

 

๋จผ์ € IfExpreession ์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ํ• ๋‹น ๋ฐ ์„ ์–ธ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด ๋•Œ์˜ curToken ๊ฐ’์€ if ๋ผ๋Š” ํ‚ค์›Œ๋“œ์ด๋‹ค. ๋งŒ์•ฝ ๋‹ค์Œ ํ† ํฐ ์ฆ‰, peekToken ๊ฐ’์ด <CONDITION>์˜ ์‹œ์ž‘์ธ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ผ ๊ฒฝ์šฐ์—๋งŒ ๊ณ„์† ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•œ๋‹ค. ์ด ๋•Œ, expectPeek ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ curToken, peekToken์„ ํ•˜๋‚˜์”ฉ ์˜ฎ๊ธด๋‹ค๋Š” ์‚ฌ์‹ค์„ ์žŠ์ง€๋ง์ž! ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ์ง€ ๊ฒ€์‚ฌ๊ฐ€ ๋๋‚œ ํ›„, ๋‹ค์‹œ ํ•œ๋ฒˆ curToken, peekToken์„ ํ•˜๋‚˜์”ฉ ์ด๋™์‹œํ‚จ๋‹ค. ์ด์ œ curToken์€ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ ๋‹ค์Œ์— ์žˆ๋Š” ์ฒซ๋ฒˆ์งธ ํ† ํฐ์— ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ด ์‹œ์ ์— ์กฐ๊ฑด์‹ ์ฆ‰, <CONDITION>์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด ์ด์ „์— ์‚ดํŽด๋ณด์•˜๋˜ parseExpression ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ๋•Œ, ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๋Š” ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฐ’์ด LOWEST๋ฅผ ์ง€์ •ํ•ด ๋„˜๊ธด๋‹ค. ์ด๋ ‡๊ฒŒ <CONDITION> ํŒŒ์‹ฑ์ด ๋๋‚ฌ์œผ๋ฉด ๋‹ค์Œ์œผ๋กœ๋Š” if์˜ body ๋ธ”๋ก์„ ํŒŒ์‹ฑํ•  ์ฐจ๋ก€๋‹ค. ๋งŒ์•ฝ ์„ฑ๊ณต์ ์œผ๋กœ <CONDITION> ํŒŒ์‹ฑ์ด ๋๋‚ฌ๋‹ค๋ฉด curToken์€ ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ์˜ ์ง์ „ ํ† ํฐ ์œ„์น˜์— ์žˆ์„ ๊ฒƒ์ด๋‹ค.

 

์ด์ œ ๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค. ์ด ๋•Œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ expectPeek ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ curToken, peekToken์„ ํ•˜๋‚˜์”ฉ ์˜ฎ๊ธด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•œ๋ฒˆ expectPeek ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ํ•œ์นธ์”ฉ ๋˜ ์˜ฎ๊ธด๋‹ค. ๋ฌผ๋ก  ์ด๋ฒˆ์— expectPeek ๋ฉ”์†Œ๋“œ๋Š” ๋‹ค์Œ ํ† ํฐ์ด body ๋ธ”๋ก์˜ ์‹œ์ž‘์„ ์˜๋ฏธํ•˜๋Š” ์™ผ์ชฝ ์ค‘๊ด„ํ˜ธ์ธ์ง€๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด curToken์˜ ์œ„์น˜๋Š” ์™ผ์ชฝ ์ค‘๊ด„ํ˜ธ์— ์œ„์น˜ํ•œ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋’ค, ์ด ์‹œ์ ์— <CONSEQUENCE> ๋ฅผ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด parseBlockStatement ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด์ œ parseBlockStatement ๋ฉ”์†Œ๋“œ์˜ ๋กœ์ง์„ ์‚ดํŽด๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseBlockStatement() *ast.BlockStatement {
	block := &ast.BlockStatement{Token: p.curToken}
	block.Statements = []ast.Statement{}
	
	p.nextToken()
	
	for !p.curTokenIs(token.RBRACE) && !p.curTokenIs(token.EOF) {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
        p.nextToken()
	}
	return block
}

 

์‚ฌ์‹ค ์œ„ ์†Œ์Šค์ฝ”๋“œ ๋กœ์ง์€ ์šฐ๋ฆฌ์—๊ฒŒ ๋‚ฏ์„ค์ง€๋Š” ์•Š๋‹ค. ๋ฐ”๋กœ ํŒŒ์„œ ๊ด€๋ จ ์ฒซ ๋ฒˆ์งธ ํฌ์ŠคํŒ…์—์„œ ์‚ดํŽด๋ณด์•˜๋˜ ParseProgram ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์†Œ๋“œ๋ž‘ ์ƒ๊น€์ƒˆ๊ฐ€ ๋งค์šฐ ๋น„์Šทํ•˜๋‹ค.

 

๋‹ค๋งŒ ์ฐจ์ด์ ์€ curTokenIs ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋กœ body ๋ธ”๋ก์˜ ๋์„ ์˜๋ฏธํ•˜๋Š” ์˜ค๋ฅธ์ชฝ ์ค‘๊ด„ํ˜ธ๊ฐ€ ๋‚˜์˜ฌ๋•Œ๊นŒ์ง€ ๊ณ„์† for-loop๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ ๋ช…๋ น๋ฌธ๋“ค์„ ํŒŒ์‹ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•ด์„œ if-else ์กฐ๊ฑด๋ฌธ ํ‘œํ˜„์‹ ํŒŒ์‹ฑ์ด ๋ชจ๋‘ ๋์ด๋‚ฌ๋‹ค. ํ•ด๋‹น ๋ชฉ์ฐจ ์ดˆ๋ฐ˜์— ์ž‘์„ฑํ–ˆ๋˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ž˜ ํ†ต๊ณผ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

5. ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ํŒŒ์‹ฑํ•˜๊ธฐ

๋‹ค์Œ์€ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•œ ํŒŒ์‹ฑ์ด๋‹ค. ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์€ ๋ง ๊ทธ๋Œ€๋กœ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ ํ‘œํ˜„์‹์ด๋‹ค. Monkey ์–ธ์–ด์—์„œ์˜ ์†Œ์Šค์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

fn(x, y) {
  return x + y;
}

fn() {
  return foobar + barfoo;
}

let myFunction = fn(x, y) {return x + y; }

fn() {
  return fn(x, y) { return x > y; };
}

myFunc(x, y, fn(x, y) { return x > y; });

 

์œ„์ฒ˜๋Ÿผ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€์ด๋‹ค. ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์ž์ฒด๋งŒ ์ •์˜ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์•ˆ์—์„œ๋„ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋˜๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ๋˜ํ•œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ์ฆ‰์‹œ ์–ด๋–ค ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ์ˆ˜๋„ ์žˆ๊ณ , ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์•ˆ์— ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ์ค‘์ฒฉ ์ƒํƒœ๋กœ ์ •์˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๋„ฃ์„ ์ˆ˜๋„ ์žˆ๋‹ค.

 

์šฐ๋ฆฌ๋Š” ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด 2๊ฐ€์ง€ ํฌ์ธํŠธ์— ์ง‘์ค‘ํ•ด์•ผ ํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์ŠคํŠธ์ด๋‹ค. ์–ด๋–ค ํ•จ์ˆ˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ 1๊ฐœ๋งŒ ๋ฐ›๊ฑฐ๋‚˜ 2๊ฐœ ์ด์ƒ์„ ๋ฐ›๊ฑฐ๋‚˜ ๋˜๋Š” ์•„์˜ˆ ๋ฐ›์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ํ•จ์ˆ˜์˜ ๋ชธ์ฒด์ธ body ๋ธ”๋ก ๋ถ€๋ถ„์ด๋‹ค. ์ด ๋ธ”๋ก ๋ถ€๋ถ„์€ ์ง์ „ ๋ชฉ์ฐจ์—์„œ ์‚ดํŽด๋ณธ if-else ๊ตฌ๋ฌธ์˜ body ๋ธ”๋ก๋ฌธ์„ ํŒŒ์‹ฑํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์ฆ‰, ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํ‘œํ˜„์‹์œผ๋กœ ์ •์˜ํ•œ ๋’ค, ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํŒŒ์‹ฑํ•˜๋Š” ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋งŒ ์ž‘์„ฑํ•ด ์ œ๊ณตํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ํ‘œํ˜„์‹์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

fn <PARAMETERS> <BLOCKSTATEMENT>

 

์ด์ œ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž. ๊ฐ€์žฅ ๋จผ์ € ํ•ด๋ณผ ๋ถ€๋ถ„์€ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ AST ๋…ธ๋“œ๋กœ์„œ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

// ast/ast.go

type FunctionLiteral struct {
	Token      token.Token
	Parameters []*Identifier
	Body       *BlockStatement
}

func (fl *FunctionLiteral) expressionNode()      {}
func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal }
func (fl *FunctionLiteral) String() string {
	var out bytes.Buffer
	
	params := []string{}
	for _, p := range fl.Parameters {
		params = append(params, p.String())
	}
	
	out.WriteString(fl.TokenLiteral())
	out.WriteString("(")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString(") ")
	out.WriteString(fl.Body.String())
	
	return out.String()
}

 

ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ AST ๋…ธ๋“œ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด FunctionLiteral ์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ์ด ๊ตฌ์กฐ์ฒด๋Š” 3๊ฐœ์˜ ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๋Š”๋ฐ, ํ•˜๋‚˜๋Š” ๋Š˜ ๊ทธ๋žฌ๋“ฏ์ด Token, ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธํ•˜๋Š” Parameters์ด๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์‹๋ณ„์ž๋กœ ์ •์˜๋˜๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ 0๊ฐœ ์ด์ƒ์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šฌ๋ผ์ด์Šค ํ˜•ํƒœ๋กœ ์ •์˜ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜์˜ ๋ธ”๋ก์„ ์˜๋ฏธํ•˜๋Š” Body๊ฐ€ ๋ฉค๋ฒ„๋กœ์„œ ์กด์žฌํ•œ๋‹ค.

 

์ด๋ฒˆ์—๋„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์ฝ”๋“œ ๋ธ”๋ก์„ ๋จผ์ € ์–ธ๊ธ‰ํ•œ๋‹ค. ๋กœ์ง ์ž์ฒด๋Š” ๊ทธ๋™์•ˆ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ž‘ ์—ญ์‹œ ๋‹ค๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์Šค์œฝ ํ•œ ๋ฒˆ ์ฝ์–ด๋ณด๋„๋ก ํ•˜์ž. ๋ณ„๋‹ค๋ฅธ ์„ค๋ช…์€ ํ•˜์ง€ ์•Š๊ฒ ๋‹ค.

 

// paresr/parser_test.go

func TestFunctionLiteralParsing(t *testing.T) {
	input := `fn(x, y) { x + y; }`

	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 %d statements. got=%d\n",
			1, 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])
	}

	function, ok := stmt.Expression.(*ast.FunctionLiteral)
	if !ok {
		t.Fatalf("stmt.Expression is not ast.FunctionLiteral. got=%T",
			stmt.Expression)
	}

	if len(function.Parameters) != 2 {
		t.Fatalf("function literal parameters wrong. want 2, got=%d\n",
			len(function.Parameters))
	}

	testLiteralExpression(t, function.Parameters[0], "x")
	testLiteralExpression(t, function.Parameters[1], "y")

	if len(function.Body.Statements) != 1 {
		t.Fatalf("function.Body.Statements has not 1 statements. got=%d\n",
			len(function.Body.Statements))
	}

	bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("function body stmt is not ast.ExpressionStatement. got=%T",
			function.Body.Statements[0])
	}

	testInfixExpression(t, bodyStmt.Expression, "x", "+", "y")
}

 

์ด์ œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•œ ํ† ํฐ์— ๋งž๋Š” ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ์ •์˜ํ•ด๋ณด์ž.

 

// parer/parser.go

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}
	// ํ˜„์žฌ ์ดˆ๊ธฐ token ๊ฐ’์€ ๋นˆ ๋ฌธ์ž์ด๊ธฐ ๋•Œ๋ฌธ์—, curToken, peekToken์— ํ† ํฐ์„ ๋‹ด์œผ๋ ค๋ฉด 2๋ฒˆ ์ˆ˜ํ–‰
	p.nextToken()
	p.nextToken()
     
    (... ์ƒ๋žต ...)
	p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
    
    (... ์ƒ๋žต ...)
    return p

 

์ƒˆ๋กœ์šด ํ•จ์ˆ˜์ธ parseFunctionLiteral ๋ฉ”์†Œ๋“œ๋ฅผ ๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseFunctionLiteral() ast.Expression {
	lit := &ast.FunctionLiteral{Token: p.curToken}
	
	if !p.expectPeek(token.LPAREN) {
		return nil
	}
	lit.Parameters = p.parseFunctionParameters()
	
	if !p.expectPeek(token.LBRACE) {
		return nil
	}
	
	lit.Body = p.parseBlockStatement()
	return lit
}

 

๋ฐฉ๊ธˆ ์ •์˜ํ•œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ AST ๋…ธ๋“œ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ FunctionLiteral ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•œ๋‹ค. curToken์€ ํ˜„์žฌ fn ํ† ํฐ์ด๋‹ค. expectPeek ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ peekToken์ด ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ์ง€ ํ™•์ธํ•˜๋ฉด์„œ ๋งž๋‹ค๋ฉด curToken, peekToken์„ ํ•œ ์นธ์”ฉ ์˜ฎ๊ธด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด curToken์€ ์ด์ œ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋‹ค. ์ด ์ˆœ๊ฐ„์— ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด parseFunctionParameters ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. parseFunctionParameters ๋ฉ”์†Œ๋“œ๋กœ ๋˜ ์ด๋™ํ•ด๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseFunctionParameters() []*ast.Identifier {
	identifiers := []*ast.Identifier{}

	if p.peekTokenIs(token.RPAREN) {
		p.nextToken()
		return identifiers
	}
	p.nextToken()

	ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
	identifiers = append(identifiers, ident)

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
		identifiers = append(identifiers, ident)
	}

	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	return identifiers
}

 

ํ˜„์žฌ curToken์€ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ ์ƒํƒœ์ด๋‹ค. ๋งŒ์•ฝ ๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค. ์ฆ‰, ๋ฐ”๋กœ ๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ๋ผ๋Š” ๊ฒƒ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ์—†์Œ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๊ทธ ์ฆ‰์‹œ ํ† ํฐ์„ ํ•œ์นธ์”ฉ ์˜ฎ๊ธด ๋’ค ๋นˆ ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

 

๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด nextToken ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ† ํฐ์„ ํ•œ ์นธ์”ฉ ์˜ฎ๊ธด๋‹ค. ์ฆ‰, ํ˜„์žฌ curToken์€ ์ฒซ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹๋ณ„์ž์— ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ Identifier ๊ตฌ์กฐ์ฒด ํ•˜๋‚˜๋ฅผ ์„ ์–ธ ๋ฐ ์ •์ด๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์Šฌ๋ผ์ด์Šค์— ์ด ๊ตฌ์กฐ์ฒด๋ฅผ ํ•˜๋‚˜ append ํ•œ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋’ค, ๋‹ค์Œ ํ† ํฐ์ด ์ปด๋งˆ(,)์ธ ๊ฒฝ์šฐ ๊ณ„์† for-loop์˜ body ๋ธ”๋ก์„ ํƒ„๋‹ค.(์ปด๋งˆ์ธ ์ด์œ ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ๋ถ„์ž๋ฅผ ์ปด๋งˆ๋กœ ๊ตฌ๋ถ„ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค) ๋ธ”๋ก ์•ˆ์—์„œ๋Š” nextToken 2๋ฒˆ์„ ํ˜ธ์ถœํ•ด์„œ curToken์ด ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹๋ณ„์ž๋กœ ์˜ฎ๊ธฐ๋„๋ก ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ fn(x, y, z) ์ด๋Ÿฐ ์‹์œผ๋กœ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ๋งŒ์•ฝ nextToken์„ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ์—๋Š” curToken์ด x๋’ค์˜ ์ปด๋งˆ(,)์— ์œ„์น˜ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ nextToken์„ ํ•œ๋ฒˆ ๋” ํ˜ธ์ถœํ•ด์ฃผ์–ด์„œ curToken์ด y๋ผ๋Š” ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹๋ณ„์ž๋กœ ์œ„์น˜ํ•˜๋„๋ก ํ•ด์ค€๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ์‹๋ณ„์ž์— ๋Œ€ํ•ด์„œ Identifier ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋˜ ์Šฌ๋ผ์ด์Šค์— ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ณผ์ •์„ ๋‹ค์Œ ํ† ํฐ์— ์ปด๋งˆ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋ฐ˜๋ณตํ•œ๋‹ค. ์ด for-loop๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  ๋‚œ ๋’ค, ๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ์ผ ๊ฒฝ์šฐ ๊ทธ๋™์•ˆ append ํ–ˆ๋˜ ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•œ๋‹ค.

 

์ด์ œ ๋‹ค์‹œ parseFunctionParameters๋ฅผ ํ˜ธ์ถœํ–ˆ๋˜ ํ˜ธ์ถœ์ž ํ•จ์ˆ˜์ธ parseFunctionLiteral๋„ ๋Œ์•„์™€๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseFunctionLiteral() ast.Expression {
	lit := &ast.FunctionLiteral{Token: p.curToken}
	
	if !p.expectPeek(token.LPAREN) {
		return nil
	}
	lit.Parameters = p.parseFunctionParameters()
	
	if !p.expectPeek(token.LBRACE) {
		return nil
	}
	
	lit.Body = p.parseBlockStatement()
	return lit
}

 

FunctionLiteral ๊ตฌ์กฐ์ฒด์˜ Parmeters ๋ฉค๋ฒ„์— ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ์ˆ˜ํ–‰ํ–ˆ๋‹ค. ์ด์ œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์˜ body ๋ธ”๋ก์„ ํŒŒ์‹ฑํ•  ์ฐจ๋ก€๋‹ค. ์—ฌ๊ธฐ๋Š” ์ง์ „ ๋ชฉ์ฐจ์ธ if-else ์กฐ๊ฑด ํ‘œํ˜„์‹์˜ body ๋ธ”๋ก์„ ํŒŒ์‹ฑํ–ˆ์„ ๋•Œ๋ž‘ ๋™์ผํ•˜๋‹ค. ๋‹ค์Œ ํ† ํฐ์ด ์™ผ์ชฝ ์ค‘๊ด„ํ˜ธ๊ฐ€ ๋‚˜์˜ค๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋‚˜์˜จ๋‹ค๋ฉด parseBlockStatement ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ body ๋ธ”๋ก์˜ ๋ช…๋ น๋ฌธ๋“ค์„ ํŒŒ์‹ฑํ•œ๋‹ค.

6. ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹ ํŒŒ์‹ฑํ•˜๊ธฐ

์ด์ œ ๋Œ€๋ง์˜ ๋งˆ์ง€๋ง‰ ๊ธฐ๋Šฅ์„ ํŒŒ์‹ฑ์— ์ถ”๊ฐ€ํ•ด๋ณผ ์ฐจ๋ก€๋‹ค. ๋ฐ”๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์ด๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

add(2, 3)

add(2 + 2, 3 * 3 * 3)

 

์†Œ์Šค์ฝ”๋“œ์—์„œ add๋Š” ์‹๋ณ„์ž์ด๋‹ค. ์ด ์‹๋ณ„์ž add์—๋Š” add ๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์ด ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ถ”ํ›„์— ๋ฐฐ์šธ ํ‰๊ฐ€ํ•˜๋Š” ๊ณผ์ €์—์„œ ์‹๋ณ„์ž add๋Š” ํ‰๊ฐ€ ์‹œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค. ์ฆ‰ ํŒŒ์„œ๊ฐ€ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ฝ์–ด๋‚ด๋ ค๊ฐ€๋ฉด์„œ add ๋ผ๋Š” ์‹๋ณ„์ž๋Š” ์ง€๋‚˜๊ฐ€๋ฒ„๋ฆฌ๊ณ  add๋ฅผ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด๋กœ ๋Œ€์ฒดํ•˜๊ฒŒ ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์œ„ ์†Œ์Šค์ฝ”๋“œ์˜ add(2, 3)์€ ์•„๋ž˜์ฒ˜๋Ÿผ add๊ฐ€ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด๋กœ ๋Œ€์ฒด๋œ๋‹ค.

 

# add๊ฐ€ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด๋กœ ๋Œ€์ฒด
fn(x, y) { x + y; }(2, 3)

 

๋˜ํ•œ ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์ธ์ž๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 

 

myFunc(2, 3, fn(x, y) { x + y; });

 

๊ทธ๋ž˜์„œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

<EXPRESSION>(<EXPRESSION>, <EXPRESSION>, ... , <EXPRESSION>)

 

์ด์ œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ AST ๋…ธ๋“œ๋กœ ํ‘œํ˜„ํ•ด๋ณด์ž. 

 

// ast/ast.go

type CallExpression struct {
	Token      token.Token
	Function   Expression
	Arguments  []Expression
}

func (ce *CallExpression) expressionNode()      {}
func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal }
func (ce *CallExpression) String() string {
	var out bytes.Buffer
	
	args := []string{}
	for _, a := range ce.Arguments {
		args = append(args, a.String())
	}
	
	out.WriteString(ce.Function.String())
	out.WriteString("(")
	out.WriteString(strings.Join(args, ", "))
	out.WriteString(")")
	
	return out.String()
}

 

CallExpression ์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด๋Š” 3๊ฐœ์˜ ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ํ† ํฐ, ๋‘ ๋ฒˆ์งธ๋Š” Function ์ด๋ผ๋Š” ๋ฉค๋ฒ„์ธ๋ฐ, ํ‘œํ˜„์‹์„ ์˜๋ฏธํ•˜๋Š” Expression ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์œผ๋กœ ์ •์˜๋˜์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Arguments๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ‘œํ˜„์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ด์„œ ์Šฌ๋ผ์ด์Šค ํ˜•ํƒœ์ด Expression ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์œผ๋กœ ์ •์˜๋˜์—ˆ๋‹ค. ์ด์ œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

 

// parser/parser_test.go

func TestCallExpressionParsing(t *testing.T) {
	input := "add(1, 2 * 3, 4 + 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 %d statements. got=%d\n",
			1, len(program.Statements))
	}

	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("stmt is not ast.ExpressionStatement. got=%T",
			program.Statements[0])
	}

	exp, ok := stmt.Expression.(*ast.CallExpression)
	if !ok {
		t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
			stmt.Expression)
	}

	if !testIdentifier(t, exp.Function, "add") {
		return
	}

	if len(exp.Arguments) != 3 {
		t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments))
	}

	testLiteralExpression(t, exp.Arguments[0], 1)
	testInfixExpression(t, exp.Arguments[1], 2, "*", 3)
	testInfixExpression(t, exp.Arguments[2], 4, "+", 5)
}

 

ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๋˜ ์ƒˆ๋กœ์šด ํ† ํฐ์„ ์ถ”๊ฐ€ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ธฐ์กด์— ์žˆ๋˜ ํ† ํฐ๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, add(2, 3) ๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž. add๋Š” ์‹๋ณ„์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด์— ์‹๋ณ„์ž์— ๋Œ€ํ•œ ํ† ํฐ์„ ์ •์˜ํ–ˆ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ์— ๋“ฑ์žฅํ•˜๋Š” ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ๋„ ๊ธฐ์กด์— ์‚ฌ์šฉํ–ˆ๋˜ ํ† ํฐ์ด๋‹ค. ๋‹ค๋งŒ, ์ด ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ค‘์œ„ ํ‘œํ˜„์‹ ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. 

 

๋‹ค์‹œ ํšŒ๊ณ ํ•ด๋ณด์ž. ์šฐ๋ฆฌ๋Š” ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ ์ผ๋ช… LPAREN ์ด๋ผ๋Š” ํ† ํฐ์— ๋Œ€ํ•ด์„œ ์ถ”๊ฐ€ํ–ˆ๋˜ ํŒŒ์‹ฑ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ๋ฐ”๋กœ ์ด ํฌ์ŠคํŒ…์˜ 3๋ฒˆ ๋ชฉ์ฐจ์—์„œ ์‚ดํŽด๋ณธ ๊ทธ๋ฃน ํ‘œํ˜„์‹์—์„œ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฒˆ์— ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” LPAREN ์ด๋ผ๋Š” ํ† ํฐ์— ๋Œ€ํ•ด์„œ ์ค‘์œ„ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํŒŒ์„œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ, LPAREN ํ† ํฐ์— ๋Œ€ํ•œ ์ค‘์œ„ ํ‘œํ˜„์‹ ํŒŒ์‹ฑ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

 

// parser/parser.go

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}
        p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	(... ์ƒ๋žต ...)
    
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
    
	(... ์ƒ๋žต ...)

	p.infixParseFns = make(map[token.TokenType]infixParseFn)
 
	(... ์ƒ๋žต ...)
	p.registerInfix(token.LPAREN, p.parseCallExpression)
	return p
}

 

์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ์ธ parseCallExpression์„ ์‚ดํŽด๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
	exp := &ast.CallExpression{Token: p.curToken, Function: function}
	exp.Arguments = p.parseCallArguments()
	return exp
}

 

ํŠน์ดํ•˜๊ฒŒ Expression ์ธํ„ฐํŽ˜์ด์Šค ์ฆ‰, ํ‘œํ˜„์‹์ธ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ์•„๊นŒ ๋งํ–ˆ๋˜ add ๋ผ๋Š” ์‹๋ณ„์ž๊ฐ€ ๋ฏธ๋ฆฌ ํŒŒ์‹ฑ๋œ ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  parseCallArguments ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์ธ์ž์— ๋ช…์‹œ๋˜์–ด ์žˆ๋Š” 0๊ฐœ ์ด์ƒ์˜ ํ‘œํ˜„์‹๋“ค์„ ํŒŒ์‹ฑํ•œ๋‹ค. ์ด์ œ parseCallArguments ๋ฉ”์†Œ๋“œ์˜ ์ƒ๊น€์ƒˆ๋ฅผ ๋ณด์ž.

 

// parser/parser.go

func (p *Parser) parseCallArguments() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RPAREN) {
		p.nextToken()
		return args
	}
	
	p.nextToken()
	args = append(args, p.parseExpression(LOWEST))
	
	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		args = append(args, p.parseExpression(LOWEST))
	}
	
	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	return args
}

 

ํ˜„์žฌ curToken์€ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ์ธ ์ƒํƒœ์ด๋‹ค. ๋‹ค์Œ ํ† ํฐ์ธ peekToken์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ๋กœ์ง์„ ๊ณ„์† ํƒ€๋„๋ก ํ•˜๋Š”๋ฐ, ๋จผ์ € nextToken์„ ํ˜ธ์ถœํ•ด์„œ ํ† ํฐ์˜ ์œ„์น˜๋ฅผ ํ•œ ์นธ์”ฉ ์˜ฎ๊ฒจ์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋’ค ์˜ฎ๊ฒจ์ง„ curToken์— ์žˆ๋Š” ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด parseExpression์„ ํ˜ธ์ถœํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚œ ๋’ค ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ๋‹ค์Œ ์ธ์ž๋ฅผ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ํ† ํฐ์ด ์ปด๋งˆ๊ฐ€ ๋“ฑ์žฅํ•˜์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๊ณ„์†์ ์œผ๋กœ ํ‘œํ˜„์‹ ํŒŒ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , args ๋ผ๋Š” ์Šฌ๋ผ์ด์Šค ๋ณ€์ˆ˜์— ๋‹ด์•„๋‚ธ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  for-loop ๊ตฌ๋ฌธ์„ ๋น ์ ธ ๋‚˜์™€ ๋‹ค์Œ ํ† ํฐ์ด ์˜ค๋ฅธ์ชฝ ์†Œ๊ด„ํ˜ธ์ด๋ฉด ์ฆ‰์‹œ args ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ๋งŒ ํ•ด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ํ†ต๊ณผํ•˜์ง€ ์•Š๋Š”๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ˜„์žฌ ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ ์ค‘์œ„ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•œ ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์™ผ์ชฝ ์†Œ๊ด„ํ˜ธ ์ค‘์œ„ ์—ฐ์‚ฐ์ž์˜ ์šฐ์„ ์ˆœ์œ„๋Š” ๊ฐ€์žฅ ๋†’๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์—ฐ์‚ฐ์ž๋ฅผ ์ง€์ •ํ•œ ํ›„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์ž˜ ํ†ต๊ณผํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

// 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,
	token.LPAREN:   CALL,     // ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ‘œํ˜„์‹์˜ ์ค‘์œ„ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•œ ์šฐ์„ ์ˆœ์œ„ ์„ค์ •
}

์ด๋ ‡๊ฒŒ ํ•ด์„œ ํŒŒ์„œ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ๋๋‚ฌ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ… ๋ถ€ํ„ฐ๋Š” ํŒŒ์„œ์™€ ๋ ‰์„œ๊ฐ€ ๋งŒ๋“  AST ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ‰๊ฐ€(Evaluation) ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

๋ฐ˜์‘ํ˜•