Skip to content

Commit 65a7ca9

Browse files
committed
优化
1 parent 969d7d3 commit 65a7ca9

File tree

2 files changed

+76
-86
lines changed

2 files changed

+76
-86
lines changed

basic-calculator/index.ts

Lines changed: 71 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,86 @@
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+
}
125
export default function calculate(s: string): number {
226
const tokens = tokenize(s);
327

4-
const ast = create_expression(tokens);
28+
const ast = buildExpression(tokens);
529

6-
return calculate_expression(ast);
30+
return evaluate(ast);
731
}
8-
export function calculate_expression(ast: Expression): number {
32+
export function evaluate(ast: Expression): number {
933
if (ast.type === "NumericLiteral") {
1034
return ast.value;
1135
}
1236
if (ast.type === "UnaryExpression") {
1337
if (ast.operator === "-") {
14-
return -1 * calculate_expression(ast.argument);
38+
return -1 * evaluate(ast.argument);
1539
}
1640
}
1741
if (ast.type === "BinaryExpression") {
1842
if (ast.operator === "-") {
1943
return (
20-
calculate_expression(ast.left) - calculate_expression(ast.right)
44+
evaluate(ast.left) - evaluate(ast.right)
2145
);
2246
}
2347
if (ast.operator === "*") {
2448
return (
25-
calculate_expression(ast.left) * calculate_expression(ast.right)
49+
evaluate(ast.left) * evaluate(ast.right)
2650
);
2751
}
2852
if (ast.operator === "+") {
2953
return (
30-
calculate_expression(ast.left) + calculate_expression(ast.right)
54+
evaluate(ast.left) + evaluate(ast.right)
3155
);
3256
}
3357

3458
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);
3761
const sign = Math.sign(num2) * Math.sign(num1);
3862
return sign * Math.floor(Math.abs(num1) / Math.abs(num2));
3963
//整数除法
4064
}
4165
}
4266
if (ast.type === "ParenthesizedExpression") {
43-
return calculate_expression(ast.expression);
67+
return evaluate(ast.expression);
4468
}
4569
throw Error("not support expression");
4670
}
47-
export type Tokens = Array<string | number | Tokens>;
71+
type Token = Tokens extends (infer P)[] ? P : never;
4872

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+
}
4984
export function tokenize(s: string): Tokens {
5085
const tokens: Tokens = [];
5186
const stack: Tokens[] = [tokens];
@@ -79,9 +114,7 @@ export function tokenize(s: string): Tokens {
79114
if (stack.length !== 1) throw Error("parentheses mismatch");
80115
return tokens;
81116
}
82-
83-
export function create_expression(tokens: Tokens): Expression {
84-
// console.log(tokens);
117+
export function buildExpression(tokens: Tokens): Expression {
85118
if (tokens.length === 0) {
86119
throw Error("empty expression");
87120
}
@@ -91,13 +124,7 @@ export function create_expression(tokens: Tokens): Expression {
91124

92125
const pendingleft: Expression[] = [];
93126
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);
101128
if (tokentype === TokenType.unknown) throw Error("unknown token");
102129
state = transform[state][tokentype] ?? State.unknown;
103130
if (state === State.unknown) throw Error("unknown state");
@@ -109,52 +136,10 @@ export function create_expression(tokens: Tokens): Expression {
109136
throw Error("accident token type");
110137
}
111138
}
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);
158143
if (pendingtype.length === 0 && pendingoperator.length === 0) {
159144
pendingleft.push(current_expression);
160145
} else {
@@ -181,21 +166,23 @@ export function create_expression(tokens: Tokens): Expression {
181166
}
182167
}
183168
}
169+
184170
if (state === State.binary) {
185171
pendingtype.push("BinaryExpression");
186172
if (typeof token === "string") {
187173
pendingoperator.push(token as ExpressionOperator);
174+
} else {
175+
throw Error("accident token type");
188176
}
189177
}
190178
}
191179
if (valid_end_states.includes(state) && pendingleft.length) {
192-
// console.log(JSON.stringify(pendingleft[0], null, 4));
193180
return pendingleft[0];
194181
} else {
195182
throw new Error("unexpected end state or empty expression");
196183
}
197184
}
198-
export const enum State {
185+
const enum State {
199186
"initial",
200187
"unary",
201188
"parentheses",
@@ -204,7 +191,7 @@ export const enum State {
204191
"unknown",
205192
}
206193
const valid_end_states = [State["parentheses"], State["number"]];
207-
export const enum TokenType {
194+
const enum TokenType {
208195
"number",
209196
"operator",
210197
"parentheses",
@@ -231,29 +218,30 @@ const transform: Record<State, Record<TokenType, State>> = {
231218
[TokenType.operator]: State.binary,
232219
},
233220
} 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 =
236224
| UnaryExpression["operator"]
237225
| BinaryExpression["operator"];
238-
export type Expression =
226+
type Expression =
239227
| BinaryExpression
240228
| NumericLiteral
241229
| UnaryExpression
242230
| ParenthesizedExpression;
243-
export interface ParenthesizedExpression {
231+
interface ParenthesizedExpression {
244232
type: "ParenthesizedExpression";
245233
expression: Expression;
246234
}
247-
export interface NumericLiteral {
235+
interface NumericLiteral {
248236
type: "NumericLiteral";
249237
value: number;
250238
}
251-
export interface UnaryExpression {
239+
interface UnaryExpression {
252240
type: "UnaryExpression";
253241
operator: "void" | "throw" | "delete" | "!" | "+" | "-" | "~" | "typeof";
254242
argument: Expression;
255243
}
256-
export interface BinaryExpression {
244+
interface BinaryExpression {
257245
type: "BinaryExpression";
258246
operator:
259247
| "+"
@@ -282,3 +270,9 @@ export interface BinaryExpression {
282270
left: Expression;
283271
right: Expression;
284272
}
273+
export const OperatorPriority: Record<string, number> = {
274+
"+": 12,
275+
"-": 12,
276+
"*": 13,
277+
"/": 13,
278+
};

basic-calculator/test.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { assertEquals } from "../deps.ts";
2-
import calculate, {
3-
calculate_expression,
4-
create_expression,
5-
tokenize,
6-
} from "./index.ts";
2+
import calculate, { buildExpression, evaluate, tokenize } from "./index.ts";
73

84
Deno.test("calculate-simple-expression", () => {
95
assertEquals(
106
-199 + 5998,
11-
calculate_expression({
7+
evaluate({
128
type: "BinaryExpression",
139
operator: "+",
1410
left: {
@@ -26,7 +22,7 @@ Deno.test("calculate-simple-expression", () => {
2622
Deno.test("calculate-Parenthesized-expression", () => {
2723
assertEquals(
2824
-(-199 + 5998),
29-
calculate_expression({
25+
evaluate({
3026
type: "UnaryExpression",
3127
operator: "-",
3228
argument: {
@@ -61,7 +57,7 @@ Deno.test("Parenthesized-tokenize", () => {
6157
]);
6258
});
6359
Deno.test("simple-expression", () => {
64-
assertEquals(create_expression(["-", 199, "+", 5998]), {
60+
assertEquals(buildExpression(["-", 199, "+", 5998]), {
6561
type: "BinaryExpression",
6662
operator: "+",
6763
left: {
@@ -76,7 +72,7 @@ Deno.test("simple-expression", () => {
7672
});
7773
});
7874
Deno.test("Parenthesized-expression", () => {
79-
assertEquals(create_expression(["-", ["-", 199, "+", 5998]]), {
75+
assertEquals(buildExpression(["-", ["-", 199, "+", 5998]]), {
8076
operator: "-",
8177
type: "UnaryExpression",
8278
argument: {

0 commit comments

Comments
 (0)