Skip to content

Perf by vibe#40

Open
tiye wants to merge 10 commits intomainfrom
perf
Open

Perf by vibe#40
tiye wants to merge 10 commits intomainfrom
perf

Conversation

@tiye
Copy link
Member

@tiye tiye commented Mar 10, 2026

No description provided.

@tiye tiye requested review from a team and Copilot March 10, 2026 17:07
@tiye tiye force-pushed the perf branch 2 times, most recently from a838967 to e3919cc Compare March 10, 2026 17:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on improving parser performance by eliminating intermediate allocations during lex/parse and by merging the $ and , tree-transform passes, while also adding a simple benchmark harness and updating project tooling/CI for Yarn 4.

Changes:

  • Replace the previous token-buffer-based pipeline with a single-pass lexAndBuild() parser in src/index.ts.
  • Optimize tree transforms in src/tree.ts (no-slice helpers + new combined resolveDollarComma) and add equivalence tests.
  • Add benchmarking support (src/bench.ts, tsconfig.bench.json, yarn bench) and update Yarn/CI/docs.

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/index.ts New single-pass lexer+builder and conditional $/, transform invocation.
src/tree.ts No-slice comma/dollar helpers + new combined resolveDollarComma pass.
src/parser.test.ts Adds equivalence tests to validate combined $/, behavior.
src/bench.ts Adds a benchmark runner for local perf testing.
tsconfig.bench.json Adds a bench-specific TS config (Node types, ES2022 target).
package.json Adds bench script, bumps version, updates dev deps, pins Yarn via packageManager.
Agents.md Adds contributor/agent perf/testing/tooling notes.
.yarnrc.yml Configures Yarn to use node-modules linker.
.gitignore Ignores bench artifacts and Yarn state files.
.github/workflows/upload.yaml Updates Node setup + Yarn caching/immutable installs + uses yarn compile.
.github/workflows/npm-publish.yaml Updates Node setup + Yarn caching/immutable installs + uses yarn compile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +21
```bash
yarn test # run all 22 Jest tests
yarn bench # benchmark on bundled test fixtures (×20, ~16 KB)
BENCH_FILE=/absolute/path/to/large.cirru yarn bench # benchmark on a real file
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc line hard-codes "run all 22 Jest tests", but src/parser.test.ts currently defines more than 22 tests (and later in this document it also mentions 27). Consider avoiding a specific count here (or update it) so it doesn't go stale.

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +198
| File | Purpose |
| ----------------------- | ----------------------------------------------------------- |
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner` |
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities |
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode` |
| `src/parser.test.ts` | All 22 tests |
| `src/bench.ts` | Benchmark script |
| `tsconfig.bench.json` | Separate tsconfig for bench (es2022, node types) |
| `tsconfig-compile.json` | Library build config |
| `test/cirru/*.cirru` | Test input fixtures |
| `test/ast/*.json` | Expected AST outputs |
| `lib/` | Compiled library output (committed) |
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The File Map claims lib/ is "Compiled library output (committed)", but .gitignore ignores lib/* and CI builds lib/ via yarn compile. Please clarify whether lib/ is expected to be committed or generated, and update this row accordingly to avoid confusing contributors.

Suggested change
| File | Purpose |
| ----------------------- | ----------------------------------------------------------- |
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner` |
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities |
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode` |
| `src/parser.test.ts` | All 22 tests |
| `src/bench.ts` | Benchmark script |
| `tsconfig.bench.json` | Separate tsconfig for bench (es2022, node types) |
| `tsconfig-compile.json` | Library build config |
| `test/cirru/*.cirru` | Test input fixtures |
| `test/ast/*.json` | Expected AST outputs |
| `lib/` | Compiled library output (committed) |
| File | Purpose |
| ----------------------- | ----------------------------------------------------------------------- |
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner` |
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities |
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode` |
| `src/parser.test.ts` | All 22 tests |
| `src/bench.ts` | Benchmark script |
| `tsconfig.bench.json` | Separate tsconfig for bench (es2022, node types) |
| `tsconfig-compile.json` | Library build config |
| `test/cirru/*.cirru` | Test input fixtures |
| `test/ast/*.json` | Expected AST outputs |
| `lib/` | Compiled library output (generated; not committed—run `yarn compile`) |

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +47

- Framework: Jest + `ts-jest`
- Test file: `src/parser.test.ts`
- Fixtures: `test/cirru/*.cirru` (source) + `test/ast/*.json` (expected output)
- Every optimization must keep all **27 tests passing**.
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testing section states "keep all 27 tests passing", but src/parser.test.ts currently has 26 test(...) cases. To avoid future drift, consider removing the exact number here (or ensure it matches the actual Jest test count).

Copilot uses AI. Check for mistakes.
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner` |
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities |
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode` |
| `src/parser.test.ts` | All 22 tests |
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The File Map entry for src/parser.test.ts says "All 22 tests", but the test file currently contains more than 22 test cases. Consider removing the specific number (or updating it) so the file map stays accurate over time.

Suggested change
| `src/parser.test.ts` | All 22 tests |
| `src/parser.test.ts` | Jest tests for the parser |

Copilot uses AI. Check for mistakes.
Comment on lines +231 to +244
case CHAR_LPAREN:
if (isOdd(indentCount)) throw new Error(`Invalid indentation size ${indentCount}`);
flushIndent(indentCount >> 1);
emitOpen();
state = ELexState.space;
indentCount = 0;
break;
default:
if (isOdd(indentCount)) throw new Error(`Invalid indentation size ${indentCount}`);
flushIndent(indentCount >> 1);
state = ELexState.token;
tokenStart = pointer - 1;
indentCount = 0;
break;
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ELexState.indent, a leading ) is currently treated as the start of a token (falls through to default) instead of emitting a close paren. This makes ) behave inconsistently depending on lexer state and can break inputs where a line begins with ) (it would produce a literal ")" token instead of closing a list). Add a CHAR_RPAREN case here that mirrors the CHAR_LPAREN handling (validate indent, flushIndent(...), then emitClose() and switch to space).

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +68
const emitClose = () => {
const completed = current!;
if (stack.length === 0) {
result.push(completed);
current = null;
} else {
throw new Error(`Unexpected chunk ${JSON.stringify(chunk)}`);
current = stack.pop()!;
current.push(completed);
}
}
};
};
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emitClose() uses a non-null assertion (current!) and will throw a runtime TypeError if a close paren or indentation-driven close is encountered when current is null (e.g., malformed input with an extra )). The previous implementation threw a controlled parse error for unexpected closes; consider guarding emitClose() and throwing an explicit Error (e.g. unexpected ")").

Copilot uses AI. Check for mistakes.
state = ELexState.escape;
break;
case CHAR_NEWLINE:
throw new Error("Expected newline in string");
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message thrown on newline inside a string literal is misleading: throw new Error("Expected newline in string") is triggered because a newline was encountered before the closing quote (i.e., an unterminated/invalid string). Consider updating the message to something like "Unexpected newline in string" or "Unterminated string literal" to match the actual error condition.

Suggested change
throw new Error("Expected newline in string");
throw new Error("Unexpected newline in string");

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants