Recursive downward algorithm to realize calculator

Time:2021-4-23

Using recursive downward algorithm combined with ourBKLexerImplementation of support for four operations and bracket priority calculator program.

Code list [golang]

package main

import (
    "fmt"
    "os"
    "bufio"
    "strings"
    "strconv"
    "./bklexer"
)

type Node interface {
    GetValue() float64
}

type Number struct {
    value float64
}

func NewNumber(token *BKLexer.Token) *Number {
    value, _ := strconv.ParseFloat(token.Source, 64)
    return &Number{value: value}
}

func (number *Number) GetValue() float64 {
    return number.value
}

type BinaryOpt struct {
    opt string
    lhs Node
    rhs Node
}

func NewBinaryOpt(token *BKLexer.Token, lhs Node, rhs Node) *BinaryOpt {
    return &BinaryOpt{opt: token.Source, lhs: lhs, rhs: rhs}
}

func (binaryOpt *BinaryOpt) GetValue() float64 {
    lhs, rhs := binaryOpt.lhs, binaryOpt.rhs
    switch binaryOpt.opt {
        case "+": return lhs.GetValue() + rhs.GetValue()
        case "-": return lhs.GetValue() - rhs.GetValue()
        case "*": return lhs.GetValue() * rhs.GetValue()
        case "/": return lhs.GetValue() / rhs.GetValue()
    }
    return 0
}

func parse(lexer *BKLexer.Lexer) Node {
    result := parse_binary_add(lexer)
    token := lexer.GetToken()
    if token.TType != BKLexer.TOKEN_TYPE_EOF {
        return nil
    }
    return result
}

func parse_binary_add(lexer *BKLexer.Lexer) Node {
    lhs := parse_binary_mul(lexer)
    if lhs == nil {
        return nil
    }
    token := lexer.GetToken()
    for token.Source == "+" || token.Source == "-" {
        rhs := parse_binary_mul(lexer)
        if rhs == nil {
            return nil
        }
        lhs = NewBinaryOpt(token, lhs, rhs)
        token = lexer.GetToken()
    }
    return lhs
}

func parse_binary_mul(lexer *BKLexer.Lexer) Node {
    lhs := parse_number(lexer)
    if lhs == nil {
        return nil
    }
    token := lexer.GetToken()
    for token.Source == "*" || token.Source == "/" {
        rhs := parse_number(lexer)
        if rhs == nil {
            return nil
        }
        lhs = NewBinaryOpt(token, lhs, rhs)
        token = lexer.GetToken()
    }
    return lhs
}

func parse_number(lexer *BKLexer.Lexer) Node {
    token := lexer.NextToken()
    if token.Name == "LPAR" {
        expr := parse_binary_add(lexer)
        if expr == nil {
            return nil
        }
        token := lexer.GetToken()
        if token.Name != "RPAR" {
            return nil
        }
        lexer.NextToken()
        return expr
    }
    if token.Name == "NUMBER" {
        number := NewNumber(token)
        lexer.NextToken()
        return number
    }
    return nil
}

func main() {
    fmt.Println("Hello My Calc.")

    lexer := BKLexer.NewLexer()
    lexer.AddRule("\d+\.?\d*", "NUMBER")
    lexer.AddRule("\+", "PLUS")
    lexer.AddRule("-", "MINUS")
    lexer.AddRule("\*", "MUL")
    lexer.AddRule("/", "DIV")
    lexer.AddRule("\(", "LPAR")
    lexer.AddRule("\)", "RPAR")
    lexer.AddIgnores("[ \f\t]+")

    reader := bufio.NewReader(os.Stdin)
    for true {
        fmt.Print("> ")
        inputs, _ := reader.ReadString('\n')
        inputs = strings.Trim(inputs, " \f\t\n")
        if inputs == "quit" {
            break
        }
        if inputs != "" {
            lexer.Build(inputs)
            result := parse(lexer)
            if result == nil {
                positon := lexer.GetToken().Col
                fmt.Println("error in :", positon)
                continue
            }
            fmt.Println("out =", result.GetValue())
        }

    }

    fmt.Println("bye!")
}

Run the test

➜ go calc.go 

Hello My Calc.
> 1 + (2 - 3) * 4 / 5      
out = 0.19999999999999996
> quit
bye!

Bring in the package you need

import (
    "fmt"
    "os"
    "bufio"
    "strings"
    "strconv"
    "./bklexer"
)
  • FMT printout
  • OS + bufio read user input
  • Strings handles strings
  • Strconv parsing string
  • Bklexer for lexical analysis

Define interface

type Node interface {
    GetValue() float64
}

NodeAs an interface, it is used to point to other structuresGetValueMethod can get the value of the node.

Defining digital nodes

type Number struct {
    value float64
}

func NewNumber(token *BKLexer.Token) *Number {
    value, _ := strconv.ParseFloat(token.Source, 64)
    return &Number{value: value}
}

NumberAs a node of numeric type, there are membersvalueFor storing values, useNewNumberFunction can instantiate it.

Interface method of realizing digital node

func (number *Number) GetValue() float64 {
    return number.value
}

Define operation node

type BinaryOpt struct {
    opt string
    lhs Node
    rhs Node
}

func NewBinaryOpt(token *BKLexer.Token, lhs Node, rhs Node) *BinaryOpt {
    return &BinaryOpt{opt: token.Source, lhs: lhs, rhs: rhs}
}

BinaryOptAs an operation node, membersoptRecord the operation symbol,lhsrhsThey are the contents on the left and right sides of the expression, such as 1 and 2 in 1 + 2.
useNewBinaryOptFunction instance.

Interface method to realize operation node

func (binaryOpt *BinaryOpt) GetValue() float64 {
    lhs, rhs := binaryOpt.lhs, binaryOpt.rhs
    switch binaryOpt.opt {
        case "+": return lhs.GetValue() + rhs.GetValue()
        case "-": return lhs.GetValue() - rhs.GetValue()
        case "*": return lhs.GetValue() * rhs.GetValue()
        case "/": return lhs.GetValue() / rhs.GetValue()
    }
    return 0
}

We need to operate symbols according to operationsoptjudgelhsAndrhsHow to calculate the value of.

Defines the entry function for syntax parsing

func parse(lexer *BKLexer.Lexer) Node {
    result := parse_binary_add(lexer)
    token := lexer.GetToken()
    if token.TType != BKLexer.TOKEN_TYPE_EOF {
        return nil
    }
    return result
}

Entry functionparseReceived*BKLexer.LexerType, it is sent to theparse_binary_addIn the middle,
Try to resolve an operation with the same level of operation as the addition operation.
Finally, judge the current situationtokenWhether it is the end. If not, it returnsnilOtherwise, the parsing result is returned.

Analytic functions that define addition level operations

func parse_binary_add(lexer *BKLexer.Lexer) Node {
    lhs := parse_binary_mul(lexer)
    if lhs == nil {
        return nil
    }
    token := lexer.GetToken()
    for token.Source == "+" || token.Source == "-" {
        rhs := parse_binary_mul(lexer)
        if rhs == nil {
            return nil
        }
        lhs = NewBinaryOpt(token, lhs, rhs)
        token = lexer.GetToken()
    }
    return lhs
}

When the function receives an argumentlexerAnd the runtime will try to resolve the multiplication level operation first,
If the parsing is completed and returned successfully, the currenttokenThe literal quantity of determines whether the addition level operation node needs to be constructed,
If necessary, try to perform another multiplication level operation parsing. If the parsing is successful, it is based on theoptConstructs the node object of the current operation level.

Analytic functions that define multiplication level operations

func parse_binary_mul(lexer *BKLexer.Lexer) Node {
    lhs := parse_number(lexer)
    if lhs == nil {
        return nil
    }
    token := lexer.GetToken()
    for token.Source == "*" || token.Source == "/" {
        rhs := parse_number(lexer)
        if rhs == nil {
            return nil
        }
        lhs = NewBinaryOpt(token, lhs, rhs)
        token = lexer.GetToken()
    }
    return lhs
}

parse_binary_mulBasic andparse_binary_addIt’s the same, except that at the beginning of the run, you try to parse the contents of a number or bracket expression,
When judging the need to build a multiplication level operation node, it is also an attempt to parse the content of another number or bracket expression.

Analytic functions that define numeric and bracket expressions

func parse_number(lexer *BKLexer.Lexer) Node {
    token := lexer.NextToken()
    if token.Name == "LPAR" {
        expr := parse_binary_add(lexer)
        if expr == nil {
            return nil
        }
        token := lexer.GetToken()
        if token.Name != "RPAR" {
            return nil
        }
        lexer.NextToken()
        return expr
    }
    if token.Name == "NUMBER" {
        number := NewNumber(token)
        lexer.NextToken()
        return number
    }
    return nil
}

Get the next one firsttokenTo determine whether it is a left bracket. If it is, it may be a bracket expression, and you need to try to parse it,
If the parsing is successful, it is necessary to judge whether the end is a right bracket. If all the parsing are successful, the parsed expression node will be returned. Otherwise, it will be returnednil
If at the beginningtokenIf it is not a left bracket but a number, a number node is instantiated and returned.
Returns if it is neither a left bracket nor a numbernil

Define lexical analyzer rules

lexer := BKLexer.NewLexer()
lexer.AddRule("\d+\.?\d*", "NUMBER")
lexer.AddRule("\+", "PLUS")
lexer.AddRule("-", "MINUS")
lexer.AddRule("\*", "MUL")
lexer.AddRule("/", "DIV")
lexer.AddRule("\(", "LPAR")
lexer.AddRule("\)", "RPAR")
lexer.AddIgnores("[ \f\t]+")

Loop read user input and analyze calculation

reader := bufio.NewReader(os.Stdin)
for true {
    fmt.Print("> ")
    inputs, _ := reader.ReadString('\n')
    inputs = strings.Trim(inputs, " \f\t\n")
    if inputs == "quit" {
        break
    }
    if inputs != "" {
        lexer.Build(inputs)
        result := parse(lexer)
        if result == nil {
            positon := lexer.GetToken().Col
            fmt.Println("error in :", positon)
            continue
        }
        fmt.Println("out =", result.GetValue())
    }
}
fmt.Println("bye!")

When analytic functionparseThe returned content is notnilCan be used whenGetValueFunction to evaluate the expression and get the result.

Part twoMake calculator support statement blockWelcome to the official account of the public housing number 100 Xiao Tong inn.