-
Notifications
You must be signed in to change notification settings - Fork 0
/
mathtoken.go
140 lines (120 loc) · 2.74 KB
/
mathtoken.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package mathtoken
import (
"errors"
"strconv"
"strings"
"unicode"
)
// Tokens defines a list of `Token`
type Tokens []Token
// Token defines a mathematical expression token
type Token struct {
Type Type
Value string
Associativity Associativity
Precedence uint
}
// Type defines token type data type
type Type uint
// List of token types
const (
TypeUnknown Type = iota
TypeSpace
TypeLParent
TypeRParent
TypeConstant
TypeVariable
TypeOperator
)
// Associativity defines token associativity data type
type Associativity uint
const (
// AssociativityNone defines a token has no associativity
AssociativityNone Associativity = iota
// AssociativityLeft defines a token is left associative
AssociativityLeft
// AssociativityRight defines a token is right associative
AssociativityRight
)
// Parse mathematical expression in infix format to `Tokens`
// and returns error if unknown token found
func Parse(s string) (tokens Tokens, err error) {
var (
buffer strings.Builder
format = func() {
if buffer.String() != "" {
token := Token{
Type: TypeVariable,
Value: buffer.String(),
Associativity: AssociativityNone,
}
if _, err := strconv.ParseFloat(buffer.String(), 64); err == nil {
token.Type = TypeConstant
}
// reset buffer
buffer.Reset()
tokens = append(tokens, token)
}
}
)
for _, c := range s {
tokenType := parse(c)
if tokenType != TypeConstant && tokenType != TypeVariable {
format()
}
switch tokenType {
case TypeSpace:
continue
case TypeLParent:
tokens = append(tokens, Token{
Type: TypeLParent,
Value: string(c),
Associativity: AssociativityNone,
})
case TypeRParent:
tokens = append(tokens, Token{
Type: TypeRParent,
Value: string(c),
Associativity: AssociativityNone,
})
case TypeConstant:
buffer.WriteRune(c)
case TypeVariable:
buffer.WriteRune(c)
case TypeOperator:
token := Token{
Type: TypeOperator,
Value: string(c),
}
switch c {
case '*', '/':
token.Precedence = 3
token.Associativity = AssociativityLeft
case '+', '-':
token.Precedence = 2
token.Associativity = AssociativityLeft
}
tokens = append(tokens, token)
case TypeUnknown:
return tokens, errors.New("mathtoken: unknown token found")
}
}
format()
return
}
func parse(r rune) Type {
if unicode.IsSpace(r) {
return TypeSpace
} else if r == '(' {
return TypeLParent
} else if r == ')' {
return TypeRParent
} else if unicode.IsDigit(r) || r == '.' {
return TypeConstant
} else if unicode.IsLetter(r) {
return TypeVariable
} else if r == '+' || r == '-' || r == '/' || r == '*' {
return TypeOperator
}
return TypeUnknown
}