'Go 언어로 만든 해석기'의 후속~ for 문구를 설치해보세요~

나는 "Go 언어로 작성된 해석기"에서 만든 해석 프로그램에 for문장을 설치해 보았다.
그나저나 지난번에 다시 대입할 수 있는 변수를 실현했다.

규격


이번에 실시된 for문장의 격식은 다음과 같다.

for ( var i = 0; i < 10; i = i + 1) { // 初期化; 継続条件; 増分処理
    puts(i); // 文
}

자구 분석


키워드에 for 를 추가합니다.
token.go
var keywords = map[string]TokenType{
    ...
    "for":    FOR,
}

문법 분석


for 문장을 나타내는 AST 노드를 정의합니다.
ast.go
type ForStatement struct {
    Token            token.Token
    InitialStatement Statement       // 初期化
    Condition        Expression      // 継続条件
    PostStatement    Statement       // 増分処理
    Block            *BlockStatement // 本体のブロック
}
그리고 다음과 같은 문법 분석을 진행한다.
parser.go
// "for"のTokenを読み込むと呼ばれる関数
func (p *Parser) parseForStatement() *ast.ForStatement {
    stmt := &ast.ForStatement{Token: p.curToken}

    // forの次は"("
    if !p.expectPeek(token.LPAREN) {
        return nil
    }
    p.nextToken()

    // 初期化の文の解析
    stmt.InitialStatement = p.parseStatement()
    p.nextToken()

    // 継続条件の式の解析
    stmt.Condition = p.parseExpression(LOWEST)
    if !p.expectPeek(token.SEMICOLON) {
        return nil
    }
    p.nextToken()

    // 増分処理の文の解析
    stmt.PostStatement = p.parseStatement()

    // 増分処理の次は")"のはず。ただし、増分処理が空の場合はすでに")"が読まれている。
    if p.peekTokenIs(token.RPAREN) {
        p.nextToken()
    }
    p.nextToken()

    // 本体の文の解析
    stmt.Block = p.parseBlockStatement()

    return stmt
}

평가


평가는 아래와 같다.
evaluator.go
// for文のNodeを評価するときに呼ばれる関数
func evalForStatement(stmt *ast.ForStatement, env *object.Environment) object.Object {
    // 初期化の文を評価
    initStmt := Eval(stmt.InitialStatement, env)
    if isError(initStmt) {
        return initStmt
    }

    var result object.Object

    for {
        // まず継続条件を評価
        condition := Eval(stmt.Condition, env)
        if isError(condition) {
            return condition
        }
        if !isTruthy(condition) {
            // 評価結果が偽だったらループを抜ける
            break
        }

        // 本体の文を評価
        result = Eval(stmt.Block, env)
        // エラーもしくはreturnで返り値が渡ってきたらそのまま返す
        if isError(result) || isReturn(result) { 
            return result
        }

        // 増分処理の文を評価
        postStmt := Eval(stmt.PostStatement, env)
        if isError(postStmt) {
            return postStmt
        }
    }

    return result
}
이제 설치가 완료되었습니다.

REPL로 해볼게요.

>> for (var i = 0; i < 10; i = i + 1) { puts(i); }       
0
1
2
3
4
5
6
7
8
9
잘 움직이는 것 같네요.

마지막


이 글은 일부 코드만 기재되어 있으니 전체적인 차이를 보고 싶은 사람은 보세요이쪽 PR..

좋은 웹페이지 즐겨찾기