Как сделать мой код парсера go более читабельным - PullRequest
1 голос
/ 30 сентября 2019

Я пишу парсер рекурсивного спуска на Go для простого выдуманного языка, поэтому я создаю грамматику по ходу дела. Мой синтаксический анализатор работает, но я хотел спросить, есть ли какие-либо рекомендации относительно того, как я должен выложить свой код или когда я должен поместить код в его собственную функцию и т. Д., Чтобы сделать его более читабельным.

I 'мы создавали парсер, следуя простым правилам, которые я выучил до сих пор, т.е. каждый нетерминал - это своя собственная функция, хотя мой код работает, я думаю, он выглядит очень грязно и нечитабельно.

Я включил код для нетерминального присваивания и грамматику над функцией.

Я взял большую часть обработки ошибок, чтобы уменьшить размер функции.

Вот несколько примеров того, что этот код может анализировать:

a = 10
a,b,c = 1,2,3
a int = 100
a,b string = "hello", "world"

Может кто-нибудь дать мне несколько советово том, как я могу сделать свой код более читабельным, пожалуйста?

// assignment                 : variable_list '=' expr_list
//                            | variable_list type
//                            | variable_list type '=' expr_list
func (p *Parser) assignment() ast.Noder {

    assignment := &ast.AssignmentNode{}

    assignment.Left = p.variable_list()

    // This if-statement deals with rule 2 or 3
    if p.currentToken.Type != token.ASSIGN {
        // Static variable declaration
        // Could be a declaration or an assignment
        // Only static variables can be declared without providing a value
        assignment.IsStatic = true

        assignment.Type = p.var_type().Value

        assignment.Right = nil

        p.nextToken()
        // Rule 2 is finished at this point in the code
        // This if-statement is for rule 3
        if p.currentToken.Type == token.ASSIGN {
            assignment.Operator = p.currentToken

            p.nextToken()

            assignment.Right = p.expr_list()
        }

    } else {
        // This deals with rule 1
        assignment.Operator = p.currentToken

        p.nextToken()

        assignment.Right = p.expr_list()

    }

    if assignment.Right == nil {
        for i := 0; i < len(assignment.Left); i++ {
            assignment.Right = append(assignment.Right, nil)
        }
    }

    if len(assignment.Left) != len(assignment.Right) {
        p.FoundError(p.syntaxError("variable mismatch, " + strconv.Itoa(len(assignment.Left)) + " on left but " + strconv.Itoa(len(assignment.Right)) + " on right,"))
    }

    return assignment
}

Ответы [ 2 ]

2 голосов
/ 30 сентября 2019

как я могу сделать мой код более читабельным?


Для удобства чтения, обязательным условием для правильного, поддерживаемого кода,

// assignment                 : variable_list '=' expr_list
//                            | variable_list type
//                            | variable_list type '=' expr_list
func (p *Parser) assignment() ast.Noder {
    assignment := &ast.AssignmentNode{}

    // variable_list
    assignment.Left = p.variable_list()

    // type
    if p.currentToken.Type != token.ASSIGN {
        // Static variable declaration
        // Could be a declaration or an assignment
        // Only static variables can be declared without providing a value
        assignment.IsStatic = true

        assignment.Type = p.var_type().Value
        p.nextToken()
    }

    // '=' expr_list
    if p.currentToken.Type == token.ASSIGN {
        assignment.Operator = p.currentToken
        p.nextToken()
        assignment.Right = p.expr_list()
    }

    // variable_list [expr_list]
    if assignment.Right == nil {
        for i := 0; i < len(assignment.Left); i++ {
            assignment.Right = append(assignment.Right, nil)
        }
    }
    if len(assignment.Left) != len(assignment.Right) {
        p.FoundError(p.syntaxError(fmt.Sprintf(
            "variable mismatch, %d on left but %d on right,",
            len(assignment.Left), len(assignment.Right),
        )))
    }

    return assignment
}

Примечание: это, вероятно, неэффективно и слишком сложно:

for i := 0; i < len(assignment.Left); i++ {
    assignment.Right = append(assignment.Right, nil)
}

Какого типа assignment.Right?

0 голосов
/ 30 сентября 2019

Что касается того, как сделать ваш код более читабельным, то здесь не всегда есть простой ответ. Я лично считаю, что код более читабелен, когда вы можете использовать имена функций вместо комментариев в коде. Многим нравится рекомендовать книгу Роберта Мартина «Чистый код». Он распространяет это по всей книге, небольшие функции, которые имеют одну цель и самодокументируются (через имя функции).

Конечно, как я уже говорил, это субъективная тема. Я взломал его и предложил приведенный ниже код, который лично мне кажется более читабельным. Он также использует имена функций для документирования происходящего. Таким образом, читателю не обязательно нужно копаться в каждом отдельном утверждении в коде, а просто в именах функций высокого уровня, если им не нужны все детали.

// assignment                 : variable_list '=' expr_list
//                            | variable_list type
//                            | variable_list type '=' expr_list
func (p *Parser) assignment() ast.Noder {
    assignment := &ast.AssignmentNode{}
    assignment.Left = p.variable_list()

    // This if-statement deals with rule 2 or 3
    if p.currentToken.Type != token.ASSIGN {
        // Static variable declaration
        // Could be a declaration or an assignment
        // Only static variables can be declared without providing a value
        p.parseStaticStatement(assignment)
    } else {
        p.parseVariableAssignment(assignment)
    }

    if assignment.Right == nil {
        assignment.appendDefaultValues()
    }

    p.checkForUnbalancedAssignment(assignment)

    return assignment
}

func (p *Parser) parseStaticStatement(assignment *ast.AssingmentNode) {
    assignment.IsStatic = true
    assignment.Type = p.var_type().Value
    assignment.Right = nil

    p.nextToken()

    // Rule 2 is finished at this point in the code
    // This if-statement is for rule 3
    if p.currentToken.Type == token.ASSIGN {
        a.parseStaticAssignment()
    } 
}

func (p *Parser) parseStaticAssignment(assignment *ast.AssignmentNode) {
    assignment.Operator = p.currentToken
    p.nextToken()
    assignment.Right = p.expr_list() 
}

func (p *Parser) parseVariableAssignment(assignment *ast.AssignmentNode) {
    // This deals with rule 1
    assignment.Operator = p.currentToken
    p.nextToken()
    assignment.Right = p.expr_list()
}

func (a *ast.AssignmentNode) appendDefaultValues() {
    for i := 0; i < len(assignment.Left); i++ {
        assignment.Right = append(assignment.Right, nil)
    }
}

func (p *Parser) checkForUnbalancedAssignment(assignment *ast.AssignmentNode) {
    if len(assignment.Left) != len(assignment.Right) {
        p.FoundError(p.syntaxError("variable mismatch, " + strconv.Itoa(len(assignment.Left)) + " on left but " + strconv.Itoa(len(assignment.Right)) + " on right,"))
    }
}

Я надеюсь, чтоВы находите это полезным. Я более чем готов ответить на любые дополнительные вопросы, которые могут у вас возникнуть, если вы оставите комментарий к моему ответу.

...