1
+ function buildParenthesizedExpression ( token : Token ) {
2
+ if ( ! Array . isArray ( token ) ) {
3
+ throw Error ( "accident token type" ) ;
4
+ }
5
+ const inner_expression = buildExpression ( token ) ;
6
+ if ( ! inner_expression ) {
7
+ throw Error ( "empty expression" ) ;
8
+ }
9
+ const current_expression : Expression = {
10
+ type : "ParenthesizedExpression" ,
11
+ expression : inner_expression ,
12
+ } ;
13
+ return current_expression ;
14
+ }
15
+ function buildNumericLiteralExpression ( token : Token ) {
16
+ if ( typeof token !== "number" ) {
17
+ throw Error ( "accident token type" ) ;
18
+ }
19
+ const current_expression : Expression = {
20
+ type : "NumericLiteral" ,
21
+ value : token ,
22
+ } ;
23
+ return current_expression ;
24
+ }
1
25
export default function calculate ( s : string ) : number {
2
26
const tokens = tokenize ( s ) ;
3
27
4
- const ast = create_expression ( tokens ) ;
28
+ const ast = buildExpression ( tokens ) ;
5
29
6
- return calculate_expression ( ast ) ;
30
+ return evaluate ( ast ) ;
7
31
}
8
- export function calculate_expression ( ast : Expression ) : number {
32
+ export function evaluate ( ast : Expression ) : number {
9
33
if ( ast . type === "NumericLiteral" ) {
10
34
return ast . value ;
11
35
}
12
36
if ( ast . type === "UnaryExpression" ) {
13
37
if ( ast . operator === "-" ) {
14
- return - 1 * calculate_expression ( ast . argument ) ;
38
+ return - 1 * evaluate ( ast . argument ) ;
15
39
}
16
40
}
17
41
if ( ast . type === "BinaryExpression" ) {
18
42
if ( ast . operator === "-" ) {
19
43
return (
20
- calculate_expression ( ast . left ) - calculate_expression ( ast . right )
44
+ evaluate ( ast . left ) - evaluate ( ast . right )
21
45
) ;
22
46
}
23
47
if ( ast . operator === "*" ) {
24
48
return (
25
- calculate_expression ( ast . left ) * calculate_expression ( ast . right )
49
+ evaluate ( ast . left ) * evaluate ( ast . right )
26
50
) ;
27
51
}
28
52
if ( ast . operator === "+" ) {
29
53
return (
30
- calculate_expression ( ast . left ) + calculate_expression ( ast . right )
54
+ evaluate ( ast . left ) + evaluate ( ast . right )
31
55
) ;
32
56
}
33
57
34
58
if ( ast . operator === "/" ) {
35
- const num1 = calculate_expression ( ast . left ) ;
36
- const num2 = calculate_expression ( ast . right ) ;
59
+ const num1 = evaluate ( ast . left ) ;
60
+ const num2 = evaluate ( ast . right ) ;
37
61
const sign = Math . sign ( num2 ) * Math . sign ( num1 ) ;
38
62
return sign * Math . floor ( Math . abs ( num1 ) / Math . abs ( num2 ) ) ;
39
63
//整数除法
40
64
}
41
65
}
42
66
if ( ast . type === "ParenthesizedExpression" ) {
43
- return calculate_expression ( ast . expression ) ;
67
+ return evaluate ( ast . expression ) ;
44
68
}
45
69
throw Error ( "not support expression" ) ;
46
70
}
47
- export type Tokens = Array < string | number | Tokens > ;
71
+ type Token = Tokens extends ( infer P ) [ ] ? P : never ;
48
72
73
+ type Tokens = Array < string | number | Tokens > ;
74
+ function getTokenType ( token : Token ) {
75
+ const tokentype : TokenType = typeof token === "number"
76
+ ? TokenType [ "number" ]
77
+ : typeof token === "string"
78
+ ? TokenType [ "operator" ]
79
+ : Array . isArray ( token )
80
+ ? TokenType [ "parentheses" ]
81
+ : TokenType [ "unknown" ] ;
82
+ return tokentype ;
83
+ }
49
84
export function tokenize ( s : string ) : Tokens {
50
85
const tokens : Tokens = [ ] ;
51
86
const stack : Tokens [ ] = [ tokens ] ;
@@ -79,9 +114,7 @@ export function tokenize(s: string): Tokens {
79
114
if ( stack . length !== 1 ) throw Error ( "parentheses mismatch" ) ;
80
115
return tokens ;
81
116
}
82
-
83
- export function create_expression ( tokens : Tokens ) : Expression {
84
- // console.log(tokens);
117
+ export function buildExpression ( tokens : Tokens ) : Expression {
85
118
if ( tokens . length === 0 ) {
86
119
throw Error ( "empty expression" ) ;
87
120
}
@@ -91,13 +124,7 @@ export function create_expression(tokens: Tokens): Expression {
91
124
92
125
const pendingleft : Expression [ ] = [ ] ;
93
126
for ( const token of tokens ) {
94
- const tokentype : TokenType = typeof token === "number"
95
- ? TokenType [ "number" ]
96
- : typeof token === "string"
97
- ? TokenType [ "operator" ]
98
- : Array . isArray ( token )
99
- ? TokenType [ "parentheses" ]
100
- : TokenType [ "unknown" ] ;
127
+ const tokentype : TokenType = getTokenType ( token ) ;
101
128
if ( tokentype === TokenType . unknown ) throw Error ( "unknown token" ) ;
102
129
state = transform [ state ] [ tokentype ] ?? State . unknown ;
103
130
if ( state === State . unknown ) throw Error ( "unknown state" ) ;
@@ -109,52 +136,10 @@ export function create_expression(tokens: Tokens): Expression {
109
136
throw Error ( "accident token type" ) ;
110
137
}
111
138
}
112
- if ( state === State . parentheses ) {
113
- if ( ! Array . isArray ( token ) ) {
114
- throw Error ( "accident token type" ) ;
115
- }
116
- const inner_expression = create_expression ( token ) ;
117
- if ( ! inner_expression ) {
118
- throw Error ( "empty expression" ) ;
119
- }
120
- const current_expression : Expression = {
121
- type : "ParenthesizedExpression" ,
122
- expression : inner_expression ,
123
- } ;
124
- if ( pendingtype . length === 0 && pendingoperator . length === 0 ) {
125
- pendingleft . push ( current_expression ) ;
126
- } else {
127
- const type = pendingtype [ pendingtype . length - 1 ] ;
128
- pendingtype . pop ( ) ;
129
- const operator = pendingoperator [ pendingoperator . length - 1 ] ;
130
- pendingoperator . pop ( ) ;
131
- if ( type === "BinaryExpression" ) {
132
- const left = pendingleft [ pendingleft . length - 1 ] ;
133
- pendingleft . pop ( ) ;
134
- pendingleft . push ( {
135
- operator : operator as BinaryExpression [ "operator" ] ,
136
- type : "BinaryExpression" ,
137
- left,
138
- right : current_expression ,
139
- } ) ;
140
- }
141
- if ( type === "UnaryExpression" ) {
142
- pendingleft . push ( {
143
- operator : operator as UnaryExpression [ "operator" ] ,
144
- type : "UnaryExpression" ,
145
- argument : current_expression ,
146
- } ) ;
147
- }
148
- }
149
- }
150
- if ( state === State . number ) {
151
- if ( typeof token !== "number" ) {
152
- throw Error ( "accident token type" ) ;
153
- }
154
- const current_expression : Expression = {
155
- type : "NumericLiteral" ,
156
- value : token ,
157
- } ;
139
+ if ( [ State . parentheses , State . number ] . includes ( state ) ) {
140
+ const current_expression : Expression = State . number === state
141
+ ? buildNumericLiteralExpression ( token )
142
+ : buildParenthesizedExpression ( token ) ;
158
143
if ( pendingtype . length === 0 && pendingoperator . length === 0 ) {
159
144
pendingleft . push ( current_expression ) ;
160
145
} else {
@@ -181,21 +166,23 @@ export function create_expression(tokens: Tokens): Expression {
181
166
}
182
167
}
183
168
}
169
+
184
170
if ( state === State . binary ) {
185
171
pendingtype . push ( "BinaryExpression" ) ;
186
172
if ( typeof token === "string" ) {
187
173
pendingoperator . push ( token as ExpressionOperator ) ;
174
+ } else {
175
+ throw Error ( "accident token type" ) ;
188
176
}
189
177
}
190
178
}
191
179
if ( valid_end_states . includes ( state ) && pendingleft . length ) {
192
- // console.log(JSON.stringify(pendingleft[0], null, 4));
193
180
return pendingleft [ 0 ] ;
194
181
} else {
195
182
throw new Error ( "unexpected end state or empty expression" ) ;
196
183
}
197
184
}
198
- export const enum State {
185
+ const enum State {
199
186
"initial" ,
200
187
"unary" ,
201
188
"parentheses" ,
@@ -204,7 +191,7 @@ export const enum State {
204
191
"unknown" ,
205
192
}
206
193
const valid_end_states = [ State [ "parentheses" ] , State [ "number" ] ] ;
207
- export const enum TokenType {
194
+ const enum TokenType {
208
195
"number" ,
209
196
"operator" ,
210
197
"parentheses" ,
@@ -231,29 +218,30 @@ const transform: Record<State, Record<TokenType, State>> = {
231
218
[ TokenType . operator ] : State . binary ,
232
219
} ,
233
220
} as Record < State , Record < TokenType , State > > ;
234
- export type ExpressionType = Expression [ "type" ] ;
235
- export type ExpressionOperator =
221
+ type ExpressionType = Expression [ "type" ] ;
222
+
223
+ type ExpressionOperator =
236
224
| UnaryExpression [ "operator" ]
237
225
| BinaryExpression [ "operator" ] ;
238
- export type Expression =
226
+ type Expression =
239
227
| BinaryExpression
240
228
| NumericLiteral
241
229
| UnaryExpression
242
230
| ParenthesizedExpression ;
243
- export interface ParenthesizedExpression {
231
+ interface ParenthesizedExpression {
244
232
type : "ParenthesizedExpression" ;
245
233
expression : Expression ;
246
234
}
247
- export interface NumericLiteral {
235
+ interface NumericLiteral {
248
236
type : "NumericLiteral" ;
249
237
value : number ;
250
238
}
251
- export interface UnaryExpression {
239
+ interface UnaryExpression {
252
240
type : "UnaryExpression" ;
253
241
operator : "void" | "throw" | "delete" | "!" | "+" | "-" | "~" | "typeof" ;
254
242
argument : Expression ;
255
243
}
256
- export interface BinaryExpression {
244
+ interface BinaryExpression {
257
245
type : "BinaryExpression" ;
258
246
operator :
259
247
| "+"
@@ -282,3 +270,9 @@ export interface BinaryExpression {
282
270
left : Expression ;
283
271
right : Expression ;
284
272
}
273
+ export const OperatorPriority : Record < string , number > = {
274
+ "+" : 12 ,
275
+ "-" : 12 ,
276
+ "*" : 13 ,
277
+ "/" : 13 ,
278
+ } ;
0 commit comments