项目文件夹

文件
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

126 行
3.6 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/**
* 表达式构建器 — 输入校验规则
* 1:1 翻译自 CalculatorExpressionBuilder.java
*
* 所有输入都在 normalized (ASCII) 空间处理
*/
import { normalize, localize, isDigit } from './tokenizer';
export type CalcState = 'INPUT' | 'EVALUATE' | 'RESULT' | 'ERROR';
/**
* 在表达式末尾追加一个字符(或函数名),返回新的 normalized 表达式
*
* @param formula 当前显示表达式
* @param token 要追加的 ASCII 字符 (e.g. '1', '+', '-', '*', '/', '.')
* @param state 当前计算器状态
* @param isEdited 是否已编辑过(用于 RESULT/ERROR 首次编辑替换)
* @returns [newFormula (localized), newIsEdited]
*/
export function appendToken(
formula: string,
token: string,
state: CalcState,
isEdited: boolean,
): [string, boolean] {
const norm = normalize(formula);
const start = norm.length;
let editStart = start;
let edited = isEdited;
// RESULT/ERROR 状态下首次编辑替换全文
if ((state === 'RESULT' || state === 'ERROR') && !edited) {
editStart = 0;
edited = true;
}
if (token === '.') {
// 查找当前数字段中是否已有小数点
const lastDotIdx = norm.lastIndexOf('.');
if (lastDotIdx >= 0) {
// 检查 lastDotIdx 之后是否全是数字(同一数字段)
const afterDot = norm.substring(lastDotIdx + 1, start);
const allDigits = [...afterDot].every(ch => isDigit(ch));
if (allDigits) {
return [formula, edited]; // 阻止重复小数点
}
}
} else if (token === '+' || token === '*' || token === '/') {
if (start === 0 && editStart === 0) {
return [formula, edited]; // 不允许前导运算符
}
// 运算符总是续算:去掉尾部已有的运算符序列后追加新运算符
// RESULT 状态下也保留公式(链式计算: 5 → 5×),不像数字那样替换
let trimEnd = start;
while (trimEnd > 0 && '+-*/'.includes(norm[trimEnd - 1])) {
trimEnd--;
}
if (trimEnd === 0) {
return [formula, edited]; // 全是运算符,不追加
}
const base = norm.substring(0, trimEnd);
return [localize(base + token), true];
}
if (token === '-') {
// 减号允许作为负号使用,也允许续算
const checkStart = start; // 总是检查末尾字符(包括 RESULT 状态)
if (checkStart > 0 && '+-'.includes(norm[checkStart - 1])) {
// 替换前一个 +/- 为 -
const base = norm.substring(0, checkStart - 1);
return [localize(base + '-'), true];
}
}
// 默认追加
if (editStart === 0 && !edited) {
// 替换整个表达式
return [localize(token), true];
}
return [localize(norm + token), true];
}
/**
* 追加函数名(自动补左括号)
* AOSP: fun_sin onClick → append "sin("
*/
export function appendFunction(
formula: string,
funcName: string,
state: CalcState,
isEdited: boolean,
): [string, boolean] {
const norm = normalize(formula);
let edited = isEdited;
if ((state === 'RESULT' || state === 'ERROR') && !edited) {
return [localize(funcName + '('), true];
}
return [localize(norm + funcName + '('), true];
}
/**
* 删除末尾一个字符
*/
export function deleteLastChar(formula: string): string {
const norm = normalize(formula);
if (norm.length === 0) return '';
// 检查是否以函数名 + ( 结尾(如 "sin("
const funcs = ['sin(', 'cos(', 'tan(', 'ln(', 'log('];
for (const fn of funcs) {
if (norm.endsWith(fn)) {
return localize(norm.slice(0, -fn.length));
}
}
// 检查是否以 √( 结尾
if (norm.endsWith('sqrt(')) {
return localize(norm.slice(0, -5));
}
return localize(norm.slice(0, -1));
}