Skip to content

Instantly share code, notes, and snippets.

@cgyy
Created January 16, 2019 06:52
Show Gist options
  • Save cgyy/eecb5094236719eb1ec57649a1251854 to your computer and use it in GitHub Desktop.
Save cgyy/eecb5094236719eb1ec57649a1251854 to your computer and use it in GitHub Desktop.
parse math expression
package main
import (
"fmt"
"math"
"strconv"
)
func main() {
var exp = NewExpression("(1.3 + 2) * 3 ^ 2 * 10 / 2")
result, err := exp.Eval()
if err != nil {
panic(err)
}
fmt.Printf("%s = %f\n", exp, result)
}
type Expression interface {
Eval() (float64, error)
}
func NewExpression(s string) Expression {
return &mathExpression{chs: []rune(s), pos: -1, ch: -1}
}
type mathExpression struct {
chs []rune
pos int
ch rune
}
func (m mathExpression) String() string {
return string(m.chs)
}
func (m *mathExpression) Eval() (float64, error) {
m.nextChar()
x, err := m.parseExpression()
if err != nil {
return 0, err
}
if m.pos < len(m.chs) {
return 0, fmt.Errorf("unexpected: %c", m.ch)
}
return x, nil
}
func (m *mathExpression) nextChar() {
m.pos++
if m.pos < len(m.chs) {
m.ch = m.chs[m.pos]
} else {
m.ch = -1
}
}
func (m *mathExpression) eat(char rune) bool {
for m.ch == ' ' {
m.nextChar()
}
if char == m.ch {
m.nextChar()
return true
}
return false
}
func (m *mathExpression) parseExpression() (float64, error) {
x, err := m.parseTerm()
if err != nil {
return 0, err
}
for {
if m.eat('+') {
x1, err := m.parseTerm()
if err != nil {
return 0, err
}
x += x1
} else if m.eat('-') {
x1, err := m.parseTerm()
if err != nil {
return 0, err
}
x -= x1
} else {
return x, nil
}
}
}
func (m *mathExpression) parseTerm() (float64, error) {
x, err := m.parseFactor()
if err != nil {
return 0, err
}
for {
if m.eat('*') {
x1, err := m.parseFactor()
if err != nil {
return 0, err
}
x *= x1
} else if m.eat('/') {
x1, err := m.parseFactor()
if err != nil {
return 0, err
}
x /= x1
} else {
return x, nil
}
}
}
func (m *mathExpression) parseFactor() (float64, error) {
if m.eat('+') {
return m.parseFactor()
}
if m.eat('-') {
x, err := m.parseFactor()
return -x, err
}
var x float64
var err error
var startPos = m.pos
if m.eat('(') {
x, err = m.parseExpression()
if err != nil {
return 0, err
}
m.eat(')')
} else if (m.ch >= '0' && m.ch <= '9') || m.ch == '.' {
for (m.ch >= '0' && m.ch <= '9') || m.ch == '.' {
m.nextChar()
}
x, err = strconv.ParseFloat(string(m.chs[startPos:m.pos]), 64)
if err != nil {
return 0, err
}
} else if m.ch >= 'a' && m.ch <= 'z' {
for m.ch >= 'a' && m.ch <= 'z' {
m.nextChar()
}
fun := string(m.chs[startPos:m.pos])
x, err = m.parseFactor()
if err != nil {
return 0, err
}
if fun == "sqrt" {
x = math.Sqrt(x)
} else if fun == "sin" {
x = math.Sin(x)
} else if fun == "cos" {
x = math.Cos(x)
} else if fun == "tan" {
x = math.Tan(x)
} else {
return 0, fmt.Errorf("unknown function :%s", fun)
}
} else {
return 0, fmt.Errorf("unexpected: %c", m.ch)
}
if m.eat('^') {
x1, err := m.parseFactor()
if err != nil {
return 0, err
}
x = math.Pow(x, x1)
}
return x, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment