captcha-ocr 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 (2026-07-24)
4
+
5
+ - Initial release of `captcha-ocr`.
6
+ - 支持 Buffer、Uint8Array、Base64 和 Data URL 输入。
7
+ - 提供可扩展的 Profile/Rule 识别规则。
8
+ - 提供 ESM、CommonJS 和 TypeScript 类型声明。
9
+ - 内置四则运算验证码识别规则。
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # captcha-ocr
2
+
3
+ **English** | [中文](README.zh-CN.md)
4
+
5
+ `captcha-ocr` is a TypeScript OCR package for image-based arithmetic captchas. It accepts image bytes or Base64 data, preprocesses difficult images, runs Tesseract OCR, and evaluates the recognized expression through a configurable profile and rule system.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 20 or newer
10
+ - A native runtime supported by [`sharp`](https://sharp.pixelplumbing.com/)
11
+
12
+ The first OCR call may download the English Tesseract training data (`eng.traineddata`). Make sure the runtime can access the network during the first initialization, or provide the training data through your Tesseract setup.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pnpm add captcha-ocr
18
+ ```
19
+
20
+ The package includes ESM, CommonJS, and TypeScript declaration builds.
21
+
22
+ ## Quick start
23
+
24
+ ```ts
25
+ import {
26
+ closeDefaultRecognizer,
27
+ recognizeCaptcha
28
+ } from 'captcha-ocr';
29
+
30
+ const result = await recognizeCaptcha(imageData);
31
+
32
+ if (result.ok) {
33
+ console.log(result.expression); // 7*9
34
+ console.log(result.answer); // 63
35
+ } else {
36
+ console.error(result.error?.code, result.error?.message);
37
+ }
38
+
39
+ await closeDefaultRecognizer();
40
+ ```
41
+
42
+ `imageData` can be any of the following:
43
+
44
+ - `Buffer`
45
+ - `Uint8Array`
46
+ - A raw Base64 string
47
+ - An image Data URL such as `data:image/png;base64,...`
48
+
49
+ ## Reuse a recognizer
50
+
51
+ For server-side workloads, create one recognizer and reuse it for multiple images. This avoids initializing an OCR worker for every request.
52
+
53
+ ```ts
54
+ import {
55
+ arithmeticProfile,
56
+ createRecognizer
57
+ } from 'captcha-ocr';
58
+
59
+ const recognizer = createRecognizer({
60
+ profile: arithmeticProfile
61
+ });
62
+
63
+ const first = await recognizer.recognize(firstImage);
64
+ const second = await recognizer.recognize(secondImage);
65
+
66
+ await recognizer.close();
67
+ ```
68
+
69
+ Call `close()` when the recognizer is no longer needed so its worker resources can be released.
70
+
71
+ ## Recognition result
72
+
73
+ ```ts
74
+ interface RecognitionResult<TAnswer> {
75
+ ok: boolean;
76
+ profileId: string;
77
+ ocrText: string;
78
+ normalizedText: string;
79
+ expression: string | null;
80
+ answer: TAnswer | null;
81
+ error: {
82
+ code: string;
83
+ message: string;
84
+ } | null;
85
+ ocrVariant: 'original' | 'threshold';
86
+ durationMs: number;
87
+ source: {
88
+ width: number | null;
89
+ height: number | null;
90
+ format: string | null;
91
+ };
92
+ preprocess: {
93
+ mode: 'original' | 'threshold';
94
+ scale: number;
95
+ threshold: number | null;
96
+ };
97
+ processedImage?: Buffer;
98
+ }
99
+ ```
100
+
101
+ The processed image is omitted by default. Enable it only when you need to inspect preprocessing results:
102
+
103
+ ```ts
104
+ const result = await recognizer.recognize(imageData, {
105
+ includeProcessedImage: true
106
+ });
107
+
108
+ console.log(result.processedImage);
109
+ ```
110
+
111
+ ## Built-in arithmetic rule
112
+
113
+ The default `arithmeticProfile` supports:
114
+
115
+ - `+`
116
+ - `-`
117
+ - `*`
118
+ - `/`
119
+
120
+ Common OCR variants such as `x`, `X`, `×`, and `·` are normalized to `*`; `÷` is normalized to `/`. Expressions are evaluated with a fixed parser and do not use `eval`.
121
+
122
+ You can import the arithmetic rule directly:
123
+
124
+ ```ts
125
+ import {
126
+ arithmeticProfile,
127
+ createArithmeticRule
128
+ } from 'captcha-ocr/rules/arithmetic';
129
+ ```
130
+
131
+ ## Custom profiles and rules
132
+
133
+ The recognizer is not limited to arithmetic. A profile defines OCR options and preprocessing, while a rule normalizes, parses, and evaluates recognized text.
134
+
135
+ ```ts
136
+ import {
137
+ createRecognizer,
138
+ type CaptchaProfile,
139
+ type CaptchaRule
140
+ } from 'captcha-ocr';
141
+
142
+ interface ParsedCode {
143
+ value: string;
144
+ }
145
+
146
+ const rule: CaptchaRule<ParsedCode, string> = {
147
+ id: 'fixed-code-v1',
148
+
149
+ normalize(text) {
150
+ return text.trim().toUpperCase();
151
+ },
152
+
153
+ parse(text) {
154
+ const normalized = this.normalize(text);
155
+
156
+ if (!/^[A-F0-9]{4}$/.test(normalized)) {
157
+ return {
158
+ ok: false,
159
+ normalized,
160
+ error: {
161
+ code: 'CODE_NOT_MATCHED',
162
+ message: 'The value is not a four-character hexadecimal code.'
163
+ }
164
+ };
165
+ }
166
+
167
+ return {
168
+ ok: true,
169
+ normalized,
170
+ expression: normalized,
171
+ parsed: {
172
+ value: normalized
173
+ }
174
+ };
175
+ },
176
+
177
+ evaluate(parsed) {
178
+ return parsed.value;
179
+ }
180
+ };
181
+
182
+ const profile: CaptchaProfile<ParsedCode, string> = {
183
+ id: 'fixed-code-v1',
184
+ ocr: {
185
+ language: 'eng',
186
+ whitelist: '0123456789ABCDEF',
187
+ pageSegMode: 7
188
+ },
189
+ rule
190
+ };
191
+
192
+ const recognizer = createRecognizer({ profile });
193
+ const result = await recognizer.recognize(imageData);
194
+ await recognizer.close();
195
+ ```
196
+
197
+ This keeps the OCR pipeline reusable while allowing each captcha format to define its own parsing and evaluation behavior.
198
+
199
+ ## CommonJS
200
+
201
+ ```js
202
+ const {
203
+ arithmeticProfile,
204
+ createRecognizer
205
+ } = require('captcha-ocr');
206
+ ```
207
+
208
+ ## Error handling
209
+
210
+ - OCR may succeed while the configured rule fails to parse the text. In that case, the result has `ok: false` and a structured `error`.
211
+ - Invalid image input throws an `ImageInputError`.
212
+ - OCR worker initialization, image decoding, and native dependency errors are thrown to the caller and should be handled according to the host application's needs.
213
+
214
+ ## Local demo
215
+
216
+ The repository also contains a small Web UI and CLI for manual testing. They are development tools and are not included in the published package.
217
+
218
+ ```bash
219
+ pnpm install
220
+ pnpm start
221
+ ```
222
+
223
+ Open <http://127.0.0.1:3000> after the server starts.
@@ -0,0 +1,223 @@
1
+ # captcha-ocr
2
+
3
+ [English](README.md) | **中文**
4
+
5
+ `captcha-ocr` 是一个用于图形算术验证码识别的 TypeScript OCR 包。它支持图片数据和 Base64 输入,可以对复杂图片进行预处理,使用 Tesseract OCR 识别文本,并通过可配置的 Profile 和 Rule 系统解析、计算识别结果。
6
+
7
+ ## 环境要求
8
+
9
+ - Node.js 20 或更高版本
10
+ - [`sharp`](https://sharp.pixelplumbing.com/) 支持的原生运行环境
11
+
12
+ 首次调用 OCR 时,Tesseract 可能会下载英文训练数据(`eng.traineddata`)。请确保运行环境在首次初始化时可以访问网络,或者通过 Tesseract 配置提供训练数据。
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pnpm add captcha-ocr
18
+ ```
19
+
20
+ 包内同时提供 ESM、CommonJS 和 TypeScript 类型声明。
21
+
22
+ ## 快速开始
23
+
24
+ ```ts
25
+ import {
26
+ closeDefaultRecognizer,
27
+ recognizeCaptcha
28
+ } from 'captcha-ocr';
29
+
30
+ const result = await recognizeCaptcha(imageData);
31
+
32
+ if (result.ok) {
33
+ console.log(result.expression); // 7*9
34
+ console.log(result.answer); // 63
35
+ } else {
36
+ console.error(result.error?.code, result.error?.message);
37
+ }
38
+
39
+ await closeDefaultRecognizer();
40
+ ```
41
+
42
+ `imageData` 支持以下类型:
43
+
44
+ - `Buffer`
45
+ - `Uint8Array`
46
+ - 纯 Base64 字符串
47
+ - 图片 Data URL,例如 `data:image/png;base64,...`
48
+
49
+ ## 复用识别器
50
+
51
+ 服务端连续识别多张图片时,建议创建一个识别器并复用,避免为每个请求重复初始化 OCR Worker。
52
+
53
+ ```ts
54
+ import {
55
+ arithmeticProfile,
56
+ createRecognizer
57
+ } from 'captcha-ocr';
58
+
59
+ const recognizer = createRecognizer({
60
+ profile: arithmeticProfile
61
+ });
62
+
63
+ const first = await recognizer.recognize(firstImage);
64
+ const second = await recognizer.recognize(secondImage);
65
+
66
+ await recognizer.close();
67
+ ```
68
+
69
+ 识别器不再使用时,请调用 `close()` 释放 Worker 资源。
70
+
71
+ ## 识别结果
72
+
73
+ ```ts
74
+ interface RecognitionResult<TAnswer> {
75
+ ok: boolean;
76
+ profileId: string;
77
+ ocrText: string;
78
+ normalizedText: string;
79
+ expression: string | null;
80
+ answer: TAnswer | null;
81
+ error: {
82
+ code: string;
83
+ message: string;
84
+ } | null;
85
+ ocrVariant: 'original' | 'threshold';
86
+ durationMs: number;
87
+ source: {
88
+ width: number | null;
89
+ height: number | null;
90
+ format: string | null;
91
+ };
92
+ preprocess: {
93
+ mode: 'original' | 'threshold';
94
+ scale: number;
95
+ threshold: number | null;
96
+ };
97
+ processedImage?: Buffer;
98
+ }
99
+ ```
100
+
101
+ 默认不会返回处理后的图片。调试预处理结果时,可以显式开启:
102
+
103
+ ```ts
104
+ const result = await recognizer.recognize(imageData, {
105
+ includeProcessedImage: true
106
+ });
107
+
108
+ console.log(result.processedImage);
109
+ ```
110
+
111
+ ## 内置算术规则
112
+
113
+ 默认的 `arithmeticProfile` 支持:
114
+
115
+ - `+`
116
+ - `-`
117
+ - `*`
118
+ - `/`
119
+
120
+ 常见 OCR 变体会自动归一化:`x`、`X`、`×` 和 `·` 会转换为 `*`,`÷` 会转换为 `/`。表达式使用固定解析器计算,不会调用 `eval`。
121
+
122
+ 也可以单独导入算术规则:
123
+
124
+ ```ts
125
+ import {
126
+ arithmeticProfile,
127
+ createArithmeticRule
128
+ } from 'captcha-ocr/rules/arithmetic';
129
+ ```
130
+
131
+ ## 自定义 Profile 和 Rule
132
+
133
+ 识别器不限于算术验证码。Profile 定义 OCR 参数和预处理方式,Rule 负责对识别文本进行归一化、解析和计算。
134
+
135
+ ```ts
136
+ import {
137
+ createRecognizer,
138
+ type CaptchaProfile,
139
+ type CaptchaRule
140
+ } from 'captcha-ocr';
141
+
142
+ interface ParsedCode {
143
+ value: string;
144
+ }
145
+
146
+ const rule: CaptchaRule<ParsedCode, string> = {
147
+ id: 'fixed-code-v1',
148
+
149
+ normalize(text) {
150
+ return text.trim().toUpperCase();
151
+ },
152
+
153
+ parse(text) {
154
+ const normalized = this.normalize(text);
155
+
156
+ if (!/^[A-F0-9]{4}$/.test(normalized)) {
157
+ return {
158
+ ok: false,
159
+ normalized,
160
+ error: {
161
+ code: 'CODE_NOT_MATCHED',
162
+ message: '无法识别为四位十六进制编码。'
163
+ }
164
+ };
165
+ }
166
+
167
+ return {
168
+ ok: true,
169
+ normalized,
170
+ expression: normalized,
171
+ parsed: {
172
+ value: normalized
173
+ }
174
+ };
175
+ },
176
+
177
+ evaluate(parsed) {
178
+ return parsed.value;
179
+ }
180
+ };
181
+
182
+ const profile: CaptchaProfile<ParsedCode, string> = {
183
+ id: 'fixed-code-v1',
184
+ ocr: {
185
+ language: 'eng',
186
+ whitelist: '0123456789ABCDEF',
187
+ pageSegMode: 7
188
+ },
189
+ rule
190
+ };
191
+
192
+ const recognizer = createRecognizer({ profile });
193
+ const result = await recognizer.recognize(imageData);
194
+ await recognizer.close();
195
+ ```
196
+
197
+ 这样可以复用统一的 OCR 流程,同时为不同验证码格式定义独立的解析和计算行为。
198
+
199
+ ## CommonJS
200
+
201
+ ```js
202
+ const {
203
+ arithmeticProfile,
204
+ createRecognizer
205
+ } = require('captcha-ocr');
206
+ ```
207
+
208
+ ## 错误处理
209
+
210
+ - OCR 成功但规则无法解析文本时,结果会返回 `ok: false` 和结构化的 `error`。
211
+ - 输入不是有效图片数据时,会抛出 `ImageInputError`。
212
+ - OCR Worker 初始化、图片解码或原生依赖异常会抛出错误,调用方应根据业务需要处理。
213
+
214
+ ## 本地 Demo
215
+
216
+ 仓库还提供用于人工测试的 Web UI 和 CLI,它们属于开发工具,不会包含在发布包中。
217
+
218
+ ```bash
219
+ pnpm install
220
+ pnpm start
221
+ ```
222
+
223
+ 服务启动后打开 <http://127.0.0.1:3000>。
@@ -0,0 +1,113 @@
1
+ type ImageInput = Buffer | Uint8Array | string;
2
+ interface RecognitionError {
3
+ code: string;
4
+ message: string;
5
+ }
6
+ interface OcrLogEntry {
7
+ status: string;
8
+ progress?: number;
9
+ [key: string]: unknown;
10
+ }
11
+ interface OcrProfile {
12
+ language: string;
13
+ whitelist: string;
14
+ pageSegMode: number | string;
15
+ preserveInterwordSpaces?: boolean;
16
+ }
17
+ interface PreprocessProfile {
18
+ scale?: number;
19
+ thresholds?: number[];
20
+ }
21
+ interface RuleParseSuccess<TParsed> {
22
+ ok: true;
23
+ normalized: string;
24
+ expression: string;
25
+ parsed: TParsed;
26
+ }
27
+ interface RuleParseFailure {
28
+ ok: false;
29
+ normalized: string;
30
+ error: RecognitionError;
31
+ }
32
+ type RuleParseResult<TParsed> = RuleParseSuccess<TParsed> | RuleParseFailure;
33
+ interface CaptchaRule<TParsed, TAnswer> {
34
+ id: string;
35
+ normalize(text: string): string;
36
+ parse(text: string): RuleParseResult<TParsed>;
37
+ evaluate(parsed: TParsed): TAnswer;
38
+ }
39
+ interface CaptchaProfile<TParsed, TAnswer> {
40
+ id: string;
41
+ ocr?: Partial<OcrProfile>;
42
+ preprocess?: PreprocessProfile;
43
+ rule: CaptchaRule<TParsed, TAnswer>;
44
+ }
45
+ interface RecognizeOptions {
46
+ thresholds?: number[];
47
+ scale?: number;
48
+ includeProcessedImage?: boolean;
49
+ }
50
+ interface ImageSource {
51
+ width: number | null;
52
+ height: number | null;
53
+ format: string | null;
54
+ }
55
+ interface PreprocessInfo {
56
+ mode: 'original' | 'threshold';
57
+ scale: number;
58
+ threshold: number | null;
59
+ }
60
+ interface RecognitionResult<TAnswer> {
61
+ ok: boolean;
62
+ profileId: string;
63
+ ocrText: string;
64
+ normalizedText: string;
65
+ expression: string | null;
66
+ answer: TAnswer | null;
67
+ error: RecognitionError | null;
68
+ ocrVariant: PreprocessInfo['mode'];
69
+ durationMs: number;
70
+ source: ImageSource;
71
+ preprocess: PreprocessInfo;
72
+ processedImage?: Buffer;
73
+ }
74
+
75
+ interface ArithmeticExpression {
76
+ left: number;
77
+ operator: string;
78
+ right: number;
79
+ }
80
+ interface ArithmeticOperator {
81
+ aliases?: string[];
82
+ evaluate(left: number, right: number): number;
83
+ validate?(left: number, right: number): RecognitionError | null;
84
+ }
85
+ declare const DEFAULT_OPERATORS: Record<string, ArithmeticOperator>;
86
+ interface ArithmeticRuleOptions {
87
+ id?: string;
88
+ whitelist?: string;
89
+ operators?: Record<string, ArithmeticOperator>;
90
+ }
91
+ declare function createArithmeticRule(options?: ArithmeticRuleOptions): CaptchaRule<ArithmeticExpression, number>;
92
+ declare const arithmeticRule: CaptchaRule<ArithmeticExpression, number>;
93
+ interface LegacyExpressionSuccess {
94
+ ok: true;
95
+ normalized: string;
96
+ expression: string;
97
+ answer: number;
98
+ error: null;
99
+ }
100
+ interface LegacyExpressionFailure {
101
+ ok: false;
102
+ normalized: string;
103
+ expression: null;
104
+ answer: null;
105
+ error: RecognitionError;
106
+ }
107
+ type LegacyExpressionResult = LegacyExpressionSuccess | LegacyExpressionFailure;
108
+ declare function normalizeOcrText(text: string): string;
109
+ declare function parseExpression(text: string): LegacyExpressionResult;
110
+ declare function calculate(left: number, operator: string, right: number): number;
111
+ declare const arithmeticProfile: CaptchaProfile<ArithmeticExpression, number>;
112
+
113
+ export { type ArithmeticExpression as A, type CaptchaProfile as C, DEFAULT_OPERATORS as D, type ImageInput as I, type LegacyExpressionFailure as L, type OcrLogEntry as O, type PreprocessInfo as P, type RecognizeOptions as R, type RecognitionResult as a, type ImageSource as b, type PreprocessProfile as c, type ArithmeticOperator as d, type ArithmeticRuleOptions as e, type CaptchaRule as f, type LegacyExpressionResult as g, type LegacyExpressionSuccess as h, type OcrProfile as i, type RecognitionError as j, type RuleParseFailure as k, type RuleParseResult as l, type RuleParseSuccess as m, arithmeticProfile as n, arithmeticRule as o, calculate as p, createArithmeticRule as q, normalizeOcrText as r, parseExpression as s };
@@ -0,0 +1,113 @@
1
+ type ImageInput = Buffer | Uint8Array | string;
2
+ interface RecognitionError {
3
+ code: string;
4
+ message: string;
5
+ }
6
+ interface OcrLogEntry {
7
+ status: string;
8
+ progress?: number;
9
+ [key: string]: unknown;
10
+ }
11
+ interface OcrProfile {
12
+ language: string;
13
+ whitelist: string;
14
+ pageSegMode: number | string;
15
+ preserveInterwordSpaces?: boolean;
16
+ }
17
+ interface PreprocessProfile {
18
+ scale?: number;
19
+ thresholds?: number[];
20
+ }
21
+ interface RuleParseSuccess<TParsed> {
22
+ ok: true;
23
+ normalized: string;
24
+ expression: string;
25
+ parsed: TParsed;
26
+ }
27
+ interface RuleParseFailure {
28
+ ok: false;
29
+ normalized: string;
30
+ error: RecognitionError;
31
+ }
32
+ type RuleParseResult<TParsed> = RuleParseSuccess<TParsed> | RuleParseFailure;
33
+ interface CaptchaRule<TParsed, TAnswer> {
34
+ id: string;
35
+ normalize(text: string): string;
36
+ parse(text: string): RuleParseResult<TParsed>;
37
+ evaluate(parsed: TParsed): TAnswer;
38
+ }
39
+ interface CaptchaProfile<TParsed, TAnswer> {
40
+ id: string;
41
+ ocr?: Partial<OcrProfile>;
42
+ preprocess?: PreprocessProfile;
43
+ rule: CaptchaRule<TParsed, TAnswer>;
44
+ }
45
+ interface RecognizeOptions {
46
+ thresholds?: number[];
47
+ scale?: number;
48
+ includeProcessedImage?: boolean;
49
+ }
50
+ interface ImageSource {
51
+ width: number | null;
52
+ height: number | null;
53
+ format: string | null;
54
+ }
55
+ interface PreprocessInfo {
56
+ mode: 'original' | 'threshold';
57
+ scale: number;
58
+ threshold: number | null;
59
+ }
60
+ interface RecognitionResult<TAnswer> {
61
+ ok: boolean;
62
+ profileId: string;
63
+ ocrText: string;
64
+ normalizedText: string;
65
+ expression: string | null;
66
+ answer: TAnswer | null;
67
+ error: RecognitionError | null;
68
+ ocrVariant: PreprocessInfo['mode'];
69
+ durationMs: number;
70
+ source: ImageSource;
71
+ preprocess: PreprocessInfo;
72
+ processedImage?: Buffer;
73
+ }
74
+
75
+ interface ArithmeticExpression {
76
+ left: number;
77
+ operator: string;
78
+ right: number;
79
+ }
80
+ interface ArithmeticOperator {
81
+ aliases?: string[];
82
+ evaluate(left: number, right: number): number;
83
+ validate?(left: number, right: number): RecognitionError | null;
84
+ }
85
+ declare const DEFAULT_OPERATORS: Record<string, ArithmeticOperator>;
86
+ interface ArithmeticRuleOptions {
87
+ id?: string;
88
+ whitelist?: string;
89
+ operators?: Record<string, ArithmeticOperator>;
90
+ }
91
+ declare function createArithmeticRule(options?: ArithmeticRuleOptions): CaptchaRule<ArithmeticExpression, number>;
92
+ declare const arithmeticRule: CaptchaRule<ArithmeticExpression, number>;
93
+ interface LegacyExpressionSuccess {
94
+ ok: true;
95
+ normalized: string;
96
+ expression: string;
97
+ answer: number;
98
+ error: null;
99
+ }
100
+ interface LegacyExpressionFailure {
101
+ ok: false;
102
+ normalized: string;
103
+ expression: null;
104
+ answer: null;
105
+ error: RecognitionError;
106
+ }
107
+ type LegacyExpressionResult = LegacyExpressionSuccess | LegacyExpressionFailure;
108
+ declare function normalizeOcrText(text: string): string;
109
+ declare function parseExpression(text: string): LegacyExpressionResult;
110
+ declare function calculate(left: number, operator: string, right: number): number;
111
+ declare const arithmeticProfile: CaptchaProfile<ArithmeticExpression, number>;
112
+
113
+ export { type ArithmeticExpression as A, type CaptchaProfile as C, DEFAULT_OPERATORS as D, type ImageInput as I, type LegacyExpressionFailure as L, type OcrLogEntry as O, type PreprocessInfo as P, type RecognizeOptions as R, type RecognitionResult as a, type ImageSource as b, type PreprocessProfile as c, type ArithmeticOperator as d, type ArithmeticRuleOptions as e, type CaptchaRule as f, type LegacyExpressionResult as g, type LegacyExpressionSuccess as h, type OcrProfile as i, type RecognitionError as j, type RuleParseFailure as k, type RuleParseResult as l, type RuleParseSuccess as m, arithmeticProfile as n, arithmeticRule as o, calculate as p, createArithmeticRule as q, normalizeOcrText as r, parseExpression as s };