Compare commits
4 Commits
9f08c34225
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9aad52347b | |||
| 2dfc4ced67 | |||
| 19d4b97669 | |||
| 8a2399e890 |
@@ -1,7 +1,6 @@
|
|||||||
# 开发规则
|
# 开发规则
|
||||||
|
|
||||||
- **严禁主动修改任何项目源代码文件(如 .ts, .js, .c, .h 等)。当前工作仅限于文档编写(.md)和方案讨论。所有代码示例仅能在对话中展示,不得写入文件。**
|
- 在设计协议和API时,我们需要考虑API的移植性,尤其需要特别关注C语言,因为C语言的语法表达力不算强。当然,各类主流的编程语言都需要纳入考虑范围,包括但不限于:
|
||||||
- 在考虑API的移植性时,需要特别考虑C语言,当然,各类主流的编程语言要需要纳入考虑范围,包括但不限于:
|
|
||||||
- C++
|
- C++
|
||||||
- Java
|
- Java
|
||||||
- C#
|
- C#
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
基于上述设想,我肯定无法接受从编译技术的角度去适配每一种编程语言(例如代码插桩甚至改造编译器),因为这会导致平台的开发变得非常复杂,维护成本也会非常高。因此我的想法是设计一套语言无关的协议,用来描述各种数据结构的变化(跟踪器),因此这套协议会定义需要支持的数据结构类型,以及每一种数据结构类型需要支持的操作指令,并且我倾向于将指令设计得更加“底层”一些,类似于“原语”的概念。
|
基于上述设想,我肯定无法接受从编译技术的角度去适配每一种编程语言(例如代码插桩甚至改造编译器),因为这会导致平台的开发变得非常复杂,维护成本也会非常高。因此我的想法是设计一套语言无关的协议,用来描述各种数据结构的变化(跟踪器),因此这套协议会定义需要支持的数据结构类型,以及每一种数据结构类型需要支持的操作指令,并且我倾向于将指令设计得更加“底层”一些,类似于“原语”的概念。
|
||||||
|
|
||||||
> 例如针对顺序表结构,我可能会定义 pick(获取元素)、drop(取消获取)、patch(修改元素) 指令来替代 swap(交换元素) 操作,因为交换两个元素的本质就是 获取元素 + 修改元素,而我在其中添加 drop 指令的目的则是使可视化过程更加具体,例如 pick 指令会将其选定的元素高亮,那么 drop 指令就可以取消该元素的高亮行为。
|
> 例如针对顺序表结构,我可能会定义pick(获取元素)、drop(取消获取)、patch(修改元素)指令来替代swap(交换元素)操作,因为交换两个元素的本质就是获取元素+修改元素,而我在其中添加rop指令的目的则是使可视化过程更加具体,例如pick指令会将其选定的元素高亮,那么drop指令就可以取消该元素的高亮行为。
|
||||||
|
|
||||||
我们会先选取一种编程语言(例如TypeScript)来实现这套协议,在实现的过程中,需要考虑到未来将添加其他编程语言的支持,因此需要考虑到不同编程语言之间的差异,例如语法/类型系统差异,进而反过来优化这套协议,以使其能够更好地支持不同的编程语言,这是一个双向的过程。
|
我们会先选取一种编程语言(例如TypeScript)来实现这套协议,在实现的过程中,需要考虑到未来将添加其他编程语言的支持,因此需要考虑到不同编程语言之间的差异,例如语法/类型系统差异,进而反过来优化这套协议,以使其能够更好地支持不同的编程语言,这是一个双向的过程。
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,5 @@ Tracer 代表一个数据结构变化的跟踪器,用户可以通过调用 Tra
|
|||||||
- LinkTracer: 链表
|
- LinkTracer: 链表
|
||||||
- TreeTracer: 树
|
- TreeTracer: 树
|
||||||
- GraphTracer: 图
|
- GraphTracer: 图
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
- LogTracer: 日志
|
- LogTracer: 日志
|
||||||
- ControlTracer: 控制
|
- ControlTracer: 控制
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ const createTracerContext = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 由于JavaScript/TypeScript在语言层面缺乏统一的“程序退出钩子”机制,
|
||||||
|
// 所以我们无法在算法执行结束后自动地输出指令序列和异常信息,
|
||||||
|
// 尽管像Node.js这样的运行时提供了process.on('exit'),但是我们希望保持SDK的环境无关性,
|
||||||
|
// 所以为了不依赖于特定运行时的API,我们设计了scope方法来创建一个显式的生命周期,
|
||||||
|
// 算法逻辑将会被一个回调函数包裹,作为routine参数,
|
||||||
|
// 在routine执行完毕后,scope会向外输出指令序列和异常信息。
|
||||||
const scope = (routine: () => void | Promise<void>) => {
|
const scope = (routine: () => void | Promise<void>) => {
|
||||||
if (scopeResult.state === SCOPE_STATE.SCOPED) {
|
if (scopeResult.state === SCOPE_STATE.SCOPED) {
|
||||||
throw new ScopeError('ScopeError: Detect multiple scope.');
|
throw new ScopeError('ScopeError: Detect multiple scope.');
|
||||||
@@ -67,19 +73,24 @@ const createTracerContext = () => {
|
|||||||
// 如果routine是同步函数,那可能会抛出同步错误,所以需要用try-catch包裹
|
// 如果routine是同步函数,那可能会抛出同步错误,所以需要用try-catch包裹
|
||||||
try {
|
try {
|
||||||
const result = routine();
|
const result = routine();
|
||||||
|
// 如果routine是异步函数,需要用catch捕获异步错误
|
||||||
if (result instanceof Promise) {
|
if (result instanceof Promise) {
|
||||||
result
|
result
|
||||||
|
.then(() => {
|
||||||
|
scopeResult.state = SCOPE_STATE.SCOPED;
|
||||||
|
flush();
|
||||||
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
// 如果是ScopeError,需要抛出,否则会在嵌套调用scope时被外层scope捕获
|
||||||
if (error instanceof ScopeError) {
|
if (error instanceof ScopeError) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
scopeResult.exception.stack = (error as Error).stack;
|
scopeResult.exception.stack = (error as Error).stack;
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
scopeResult.state = SCOPE_STATE.SCOPED;
|
scopeResult.state = SCOPE_STATE.SCOPED;
|
||||||
flush();
|
flush();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 如果routine是同步函数,执行完成后需要设置scope状态
|
||||||
scopeResult.state = SCOPE_STATE.SCOPED;
|
scopeResult.state = SCOPE_STATE.SCOPED;
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,51 @@
|
|||||||
import { getTracerContext } from '../context';
|
import { getTracerContext } from '../context';
|
||||||
import type { JsonValue } from '../types';
|
import type { JsonValue } from '../types';
|
||||||
|
|
||||||
interface BaseArrayTracerCreateOptions {
|
interface ArrayTracerCreateOptions<T extends JsonValue> {
|
||||||
description?: string;
|
description?: string;
|
||||||
|
initial?: T[];
|
||||||
|
walker?: (builder: { add: (item: T) => void }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ArrayTracerCreateOptionsFromArray<
|
type ArrayTracerMetadata = {
|
||||||
T extends JsonValue[],
|
// initial: JsonValue[];
|
||||||
> extends BaseArrayTracerCreateOptions {
|
length: number;
|
||||||
array: T;
|
};
|
||||||
walker?: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArrayTracerCreateOptionsFromWalker<
|
export const createArrayTracer = <T extends JsonValue = JsonValue>(
|
||||||
T extends JsonValue[],
|
|
||||||
> extends BaseArrayTracerCreateOptions {
|
|
||||||
array?: never;
|
|
||||||
walker: (commit: (item: T[number]) => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArrayTracerCreateOptions<T extends JsonValue[]> =
|
|
||||||
| ArrayTracerCreateOptionsFromArray<T>
|
|
||||||
| ArrayTracerCreateOptionsFromWalker<T>;
|
|
||||||
|
|
||||||
export const createArrayTracer = <T extends JsonValue[]>(
|
|
||||||
options: ArrayTracerCreateOptions<T>,
|
options: ArrayTracerCreateOptions<T>,
|
||||||
) => {
|
) => {
|
||||||
const { description, array, walker } = options;
|
const { description, initial, walker } = options;
|
||||||
const tracer = crypto.randomUUID();
|
const tracer = crypto.randomUUID();
|
||||||
|
|
||||||
// 优化:仅维护数组长度作为影子状态,这在 C++/Java 等强类型语言中也极易实现(仅需一个 int 变量)
|
const metadata: ArrayTracerMetadata = {
|
||||||
// 这种“最小必要状态”策略既能实现越界校验,又避免了在强类型语言中处理泛型存储的复杂性,且内存开销极低。
|
length: 0,
|
||||||
let arrayLength = array ? array.length : 0;
|
};
|
||||||
|
|
||||||
|
const buildInitial = () => {
|
||||||
|
if (!!initial) {
|
||||||
|
return [...initial];
|
||||||
|
}
|
||||||
|
if (!!walker) {
|
||||||
|
const initial: T[] = [];
|
||||||
|
walker({
|
||||||
|
add: (item) => {
|
||||||
|
initial.push(item);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// console.log(_initial);
|
||||||
|
|
||||||
const { command } = getTracerContext();
|
const { command } = getTracerContext();
|
||||||
|
|
||||||
const validateIndex = (index: number) => {
|
const validateIndex = (index: number) => {
|
||||||
if (index < 0 || index >= arrayLength) {
|
if (index < 0 || index >= metadata.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`[ArrayTracer] Index out of bounds: index ${index} is not within [0, ${arrayLength})`,
|
`ArrayTracer: Index out of bounds: index ${index} is not within [0, ${metadata.length})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -47,18 +54,18 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'create',
|
action: 'create',
|
||||||
params: {
|
payload: {
|
||||||
description: description ?? 'ArrayTracer',
|
description: description ?? 'ArrayTracer',
|
||||||
array: array ?? [],
|
initial: buildInitial(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// size 为正时,数组长度增加;为负时,数组长度减少(但不能小于 0)
|
// size 为正时,数组长度增加;为负时,数组长度减少(但不能小于 0)
|
||||||
const scale = (size: number) => {
|
const scale = (size: number) => {
|
||||||
arrayLength += size;
|
metadata.length += size;
|
||||||
if (arrayLength < 0) {
|
if (metadata.length < 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`[ArrayTracer] Invalid size: ${size}, array length cannot be negative`,
|
`ArrayTracer: Invalid size: ${size}, array length cannot be negative`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +73,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'scale',
|
action: 'scale',
|
||||||
params: {
|
payload: {
|
||||||
size: size,
|
size: size,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -79,7 +86,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'pick',
|
action: 'pick',
|
||||||
params: {
|
payload: {
|
||||||
index: index,
|
index: index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -92,20 +99,20 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'drop',
|
action: 'drop',
|
||||||
params: {
|
payload: {
|
||||||
index: index,
|
index: index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const patch = (index: number, value: T[number]) => {
|
const patch = (index: number, value: T) => {
|
||||||
validateIndex(index);
|
validateIndex(index);
|
||||||
|
|
||||||
command({
|
command({
|
||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'patch',
|
action: 'patch',
|
||||||
params: {
|
payload: {
|
||||||
index: index,
|
index: index,
|
||||||
value: value,
|
value: value,
|
||||||
},
|
},
|
||||||
@@ -119,7 +126,7 @@ export const createArrayTracer = <T extends JsonValue[]>(
|
|||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
action: 'unset',
|
action: 'unset',
|
||||||
params: {
|
payload: {
|
||||||
index: index,
|
index: index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,36 +6,36 @@ type BaseArrayTracerCommand = BaseTracerCommand & {
|
|||||||
|
|
||||||
type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
|
type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
|
||||||
action: 'create';
|
action: 'create';
|
||||||
params: {
|
payload: {
|
||||||
description: string;
|
description: string;
|
||||||
array: JsonValue[];
|
initial: JsonValue[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
|
type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
|
||||||
action: 'scale';
|
action: 'scale';
|
||||||
params: {
|
payload: {
|
||||||
size: number;
|
size: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTracerPickCommand = BaseArrayTracerCommand & {
|
type ArrayTracerPickCommand = BaseArrayTracerCommand & {
|
||||||
action: 'pick';
|
action: 'pick';
|
||||||
params: {
|
payload: {
|
||||||
index: number;
|
index: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTracerDropCommand = BaseArrayTracerCommand & {
|
type ArrayTracerDropCommand = BaseArrayTracerCommand & {
|
||||||
action: 'drop';
|
action: 'drop';
|
||||||
params: {
|
payload: {
|
||||||
index: number;
|
index: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
|
type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
|
||||||
action: 'patch';
|
action: 'patch';
|
||||||
params: {
|
payload: {
|
||||||
index: number;
|
index: number;
|
||||||
value: JsonValue;
|
value: JsonValue;
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,7 @@ type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
|
|||||||
|
|
||||||
type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
|
type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
|
||||||
action: 'unset';
|
action: 'unset';
|
||||||
params: {
|
payload: {
|
||||||
index: number;
|
index: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user