Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
028719c
fix: import default issue with from token
imrim12 Apr 5, 2025
92a03c2
feat: Phase 0-5 — infra, full P1+P2 syntax, CLI, error messages
imrim12 Apr 20, 2026
c658742
feat: ternary, new expression, unary/binary fixes, operator tests
imrim12 Apr 20, 2026
e84a515
feat: class polish, super/new expressions, primitives, misc statements
imrim12 Apr 20, 2026
a410b00
feat: dynamic import + matrix update 95.5% complete
imrim12 Apr 20, 2026
db204f7
feat: tagged template + optional catch binding tests
imrim12 Apr 20, 2026
8d81bfc
feat: Phase 6 + 7 — plugin fixes, stdlib, VJS run with stdlib works
imrim12 Apr 20, 2026
0af56e8
feat: Node ESM loader + register hook for direct .vjs execution
imrim12 Apr 20, 2026
bf31058
feat: Bun plugin + finalize matrix at 98.5%
imrim12 Apr 20, 2026
425bef4
docs: update getting-started + add stdlib docs
imrim12 Apr 20, 2026
8ee281f
chore: scope coverage to parser + shared only
imrim12 Apr 20, 2026
7f0860d
docs: modernize basics/variables.md and basics/function.md
imrim12 Apr 20, 2026
32ee30b
docs: add CHANGELOG.md with full feature log
imrim12 Apr 20, 2026
53e7ac9
docs: rewrite basics/if.md and basics/for.md for modern features
imrim12 Apr 20, 2026
ec88ea2
docs: complete basics/ with modern features
imrim12 Apr 20, 2026
198dc35
feat: postfix operator chaining + bug fixes from full-features demo
imrim12 Apr 20, 2026
dfa41e7
test: expression chaining coverage + CallExpression postfix
imrim12 Apr 20, 2026
498db01
chore: bump all packages to 1.0.0-beta.1
imrim12 Apr 20, 2026
775a6dc
test: add errors.test.ts and expression-new.test.ts + remove custom e…
imrim12 Apr 20, 2026
8f7ef8b
test: add statement-all.test.ts + support bare BlockStatement at top …
imrim12 Apr 20, 2026
98d370f
test: tokenizer edge cases — whitespace/comments, regex context, ops
imrim12 Apr 20, 2026
0c41506
docs: add migration-from-js.md
imrim12 Apr 20, 2026
4686574
test: stdlib unit tests (15 tests)
imrim12 Apr 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 20.x
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint

test:
strategy:
fail-fast: false
matrix:
node-version: [18.x, 20.x, 22.x]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm test:coverage
- name: Upload coverage artifact
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 7

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 20.x
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build

compatibility-summary:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Generate compatibility summary
run: node scripts/compat-summary.mjs >> $GITHUB_STEP_SUMMARY
44 changes: 0 additions & 44 deletions .github/workflows/run_test.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ lerna-debug.log*

dist
node_modules
coverage

.DS_Store
.env
93 changes: 93 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Changelog

## [Unreleased]

### Added — Phase 0 (nền tảng)
- Vitest 1.6 + v8 coverage reporting, baseline ≥88% statements
- GitHub Actions CI: lint + test matrix (Node 18/20/22 × Ubuntu/Windows) + build + compat summary
- 14 Vietnamese keyword aliases: `biến`, `nhường`, `đây`, `rỗng`, `tĩnh`, `công khai`, `riêng tư`, `bảo vệ`, `lấy`, `gán`, `vô cực`, `của`, `lặp`, `là kiểu`
- `CONTRIBUTING.md` với template thêm parser node + Unicode boundary rules
- `scripts/compat-summary.mjs` — dashboard đọc `compatibility.md` + `coverage-summary.json`
- `docs/compatibility.md` + `docs/roadmap.md`

### Added — Phase 1 (cú pháp P1)
- Arrow function (single param, paren, multi, empty; expr + block body)
- Default parameters (`AssignmentPattern`)
- Rest parameters (`RestElement`) + validation vị trí
- Spread (`...`) trong array / object / call
- Destructuring object: shorthand, rename, default, rest, nested
- Destructuring array: hole, default, rest, nested, mixed
- Template literal interpolation `${...}` với nested braces
- `for...of` / `for...in` / `for await ... of`
- Object shorthand `{a}` + computed key `{[k]: v}`

### Added — Phase 2 (cú pháp P2)
- Private field `#x` (`ClassPrivateProperty`)
- Getter/setter trong class + object literal (`lấy` / `gán`)
- Yield expression + `yield*` (tiếng Việt: `nhường`)
- Optional chaining edge cases: `?.prop`, `?.[k]`, chained
- Compound assignment đầy đủ (16 operators): `+=`, `-=`, `*=`, `/=`, `%=`, `**=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, `>>>=`, `||=`, `&&=`, `??=`
- Numeric literals nâng cao: hex `0x`, octal `0o`, binary `0b`, BigInt `123n`, separator `1_000`
- Regex literal `/x/gi` với context-aware tokenization
- String escape sequences: `\n`, `\t`, `\x41`, `\u{1F600}`, v.v.
- Labeled break/continue + fix bug `ContinueStatement` ăn nhầm `BREAK` keyword
- Ternary `c ? a : b` (`ConditionalExpression`)
- `NewExpression` với args + `SpreadElement`
- `TaggedTemplateExpression`
- `SuperExpression` (call + member)
- `InfinityIdentifier`
- Dynamic import `sử dụng("path")`
- Optional catch binding `bắt lỗi {}`
- Access modifiers trong class (`công khai`/`riêng tư`/`bảo vệ`)
- Static method/property with `tĩnh` keyword
- Generator method `*foo()`
- Method detection trong class body (transition ClassProperty → ClassMethod khi thấy `(`)

### Added — Phase 3 (mangling)
- Identifier mangling mới: `xin chào` → `xin_chào` (Unicode trực tiếp, thay vì codepoint `_xin_ch224o`)

### Added — Phase 4 (error quality)
- `VietScriptError` class với `file:line:col` + snippet + caret + hint
- `Parser.filename` support

### Added — Phase 5 (CLI + runtime)
- `@vietscript/cli` package với commands: `run`, `build`, `watch`, `check`
- Source maps qua `@babel/generator`
- Node ESM loader: `node --import '@vietscript/cli/register' file.vjs`
- Bun plugin: `@vietscript/cli/bun-plugin`
- Binary: `vietscript` / `vjs`

### Added — Phase 6 (plugins)
- Fix Vite plugin: emit JS (trước đây trả về JSON AST)
- Fix Webpack plugin: same fix + source maps via `this.callback`

### Added — Phase 7 (stdlib)
- `@vietscript/stdlib` — wrappers tiếng Việt cho JS globals
- Console: `ghi`, `ghi lỗi`, `ghi cảnh báo`
- Ép kiểu: `tự số`, `tự chuỗi`, `tự bool`, `kiểu`, `độ dài`
- Math: `tròn`, `làm tròn xuống`, `làm tròn lên`, `giá trị tuyệt đối`, `nhỏ nhất`, `lớn nhất`, `ngẫu nhiên`
- Async: `đợi`
- JSON: `phân tích JSON`, `JSON sang chuỗi`
- Fetch: `tải văn bản`, `tải JSON`

### Fixed
- Tokenizer: specs order bị sai (prefix conflict `+` vs `+=`) → reorder longest-first
- Tokenizer: trailing non-ASCII char boundary (`xoá`, `chờ`, `khi mà`, `riêng tư`, `bảo vệ`) dùng negative lookahead thay vì `\b`
- Tokenizer: multi-word identifier nuốt embedded keyword (e.g. `x của arr` thành 1 identifier)
- `UnaryExpression`: dùng Keyword enum thay vì string literals
- `BinaryExpression`: support `instanceof`, `in` keyword operators + bitwise ops
- `ContinueStatement`: eat `CONTINUE` keyword thay vì `BREAK`
- `ExpressionStatement`: unwrap `LabeledStatement` khi Expression trả về statement-like
- `StatementList`: handle leading `;` as `EmptyStatement` (không infinite loop)
- `UndefinedIdentifier`: name = `"undefined"` lowercase (trước là `"Undefined"`)
- `ClassDeclaration`: support `extends` keyword (ngoài dạng paren)
- Docs: xóa JSON-based dummy output từ Vite/Webpack plugin

### Stats
- Tests: 70 → 298 (100% pass)
- Coverage: 79.85% → 92.44% statements / 71.23% → 85.88% branches / 84.89% → 96.25% functions
- Compatibility matrix: 40.8% → 98.5% complete (135/137 features)

## [0.0.1] - Initial

Initial parser skeleton, basic syntax support, playground.
151 changes: 142 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,148 @@
# Welcome to FORMKL contributing guide
# Đóng góp cho VietScript

Thank you for investing your time in contributing to our project!
Cảm ơn bạn quan tâm đến dự án! Tài liệu này hướng dẫn cách đóng góp code, đặc biệt là thêm cú pháp mới vào parser.

Read our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
Tham khảo [Code of Conduct](CODE_OF_CONDUCT.md) và [Roadmap](docs/roadmap.md).

Please make sure to read the [Contributing Guide](https://vietscript.org/learning/contribution-guide.html) before making a pull request.
## Thiết lập môi trường

Thank you to all the people who already contributed to Vue!
```bash
pnpm install
pnpm test # chạy test
pnpm test:coverage # chạy test kèm coverage report
pnpm lint # lint với eslint
pnpm build # build tất cả package
```

<a href="https://github.com/imrim12/vietscript/graphs/contributors">
<img src="https://contrib.rocks/image?repo=imrim12/vietscript" />
</a>
## Kiến trúc tổng quan

Made with [contrib.rocks](https://contrib.rocks).
```
Source .vjs → Tokenizer → Parser → Babel AST → @babel/generator → JavaScript
```

- `packages/parser/src/tokenizer.ts` — chia source thành token dựa trên `constants/specs.ts`.
- `packages/parser/src/parser.ts` — điều phối, delegate cho từng node class.
- `packages/parser/src/nodes/**` — mỗi AST node một file class, tự đọc token và build AST.
- Codegen dùng `@babel/generator` lib — không tự viết printer.

## Quy trình thêm cú pháp mới

### 1. Thêm keyword (nếu cần)

Nếu cú pháp cần keyword mới:

1. Thêm hằng vào `packages/shared/parser/keyword.enum.ts`.
2. Thêm regex tokenize vào `packages/parser/src/constants/specs.ts`. **Lưu ý Unicode:**
- Nếu từ tiếng Việt **kết thúc bằng chữ ASCII** (a-z), dùng `\b` cuối: `/^\b(english|ti\u1EBFng vi\u1EC7t_)\b/`.
- Nếu **kết thúc bằng chữ Unicode tiếng Việt** (ư, ệ, ộ...), dùng negative lookahead: `/^(english\b|ti\u1EBFng vi\u1EC7t(?![A-Za-z\u00C0-\u1EF9]))/`.
- Nếu **bắt đầu bằng chữ Unicode** (đ, â...), bỏ `\b` đầu — chỉ giữ `^` anchor.
3. Thêm test tokenize vào `packages/parser/src/__test__/vietnamese-keywords.test.ts`.

### 2. Tạo node class

Mỗi AST node là 1 file trong `packages/parser/src/nodes/<category>/<NodeName>.ts`. Template:

```ts
import { Parser } from "@parser/parser";
import { Keyword } from "@vietscript/shared";

export class MyStatement {
type = "MyStatement";

// field khai báo theo chuẩn Babel AST
foo: SomeNode;

constructor(parser: Parser) {
parser.eat(Keyword.MY_KEYWORD);
parser.eat("(");
this.foo = new SomeNode(parser);
parser.eat(")");
}
}
```

Nguyên tắc:
- **Shape phải khớp Babel AST** — nếu không `@babel/generator` sẽ sinh sai. Tra cứu [babel AST spec](https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md).
- Dùng `parser.eat(tokenType)` để consume token kỳ vọng, throw nếu không khớp.
- Không tự log hoặc thêm comment giải thích những gì code đã rõ — chỉ comment nếu có invariant ẩn.

### 3. Wire vào chỗ cần gọi

Edit file cha (ví dụ `Statement.ts` nếu là statement mới) để dispatch sang node mới dựa vào `parser.lookahead.type`.

### 4. Viết test (**bắt buộc 3 nhóm**)

Mỗi feature mới phải có đủ 3 loại test trước khi merge:

**a) Parser positive** — parse đúng, AST đúng shape.

```ts
// packages/parser/src/nodes/.../__test__/my-statement.test.ts
import { MyStatement } from "@parser/nodes/.../MyStatement";
import parser from "../../../setup-test";
import toPlainObject from "../../../toPlainObject";

describe("my-statement.test", () => {
it("should parse normally", () => {
const result = parser.parse(`từ khoá của tôi (x)`, MyStatement);
expect(toPlainObject(result)).toStrictEqual({
type: "MyStatement",
foo: { /* expected shape */ },
});
});
});
```

**b) Parser negative** — cú pháp sai throw error rõ ràng.

```ts
it("should throw on missing paren", () => {
expect(() => parser.parse(`từ khoá của tôi`, MyStatement))
.toThrowError(/expected/i);
});
```

**c) Codegen** — AST sinh ra JS string chuẩn, dùng snapshot.

```ts
// packages/parser/src/nodes/.../__test__/generator/generator-my-statement.test.ts
import generate from "@babel/generator";
import { MyStatement } from "@parser/nodes/.../MyStatement";
import parser from "../../../../setup-test";

describe("generator-my-statement.test", () => {
it("should generate javascript", () => {
const ast = parser.parse(`từ khoá của tôi (x)`, MyStatement);
const result = generate(ast);
expect(result.code).toMatchSnapshot();
});
});
```

### 5. Cập nhật compatibility matrix

Mở [docs/compatibility.md](docs/compatibility.md), tìm dòng feature tương ứng, đổi ❌ → 🟡 → ✅ theo trạng thái thực tế của PR.

### 6. Cập nhật docs

Nếu cú pháp cần hướng dẫn sử dụng, thêm/cập nhật trang tương ứng trong `docs/`.

## Quy ước code

- **Không thêm comment kể lể** "thêm cho task X", "PR #123", "lấy cảm hứng từ Y". Code nên tự giải thích qua tên biến.
- **Comment chỉ khi có lý do ẩn**: workaround bug cụ thể, invariant không nhìn thấy trong code.
- **Không thêm error handling cho scenario không thể xảy ra** — tin internal API, chỉ validate boundary.
- **Ưu tiên sửa file có sẵn** hơn tạo file mới.
- **Chạy `pnpm lint` trước khi commit**.

## Quy trình PR

1. Fork, tạo branch từ `main`: `git checkout -b feat/arrow-function`.
2. Commit theo [conventional commits](https://www.conventionalcommits.org/): `feat(parser): add arrow function`, `fix(tokenizer): ...`, `docs: ...`, `test: ...`.
3. PR phải pass CI (lint + test + coverage + build).
4. Mỗi feature trong compatibility matrix = 1 PR riêng. Không bó nhiều feature trong 1 PR.
5. Cập nhật [compatibility.md](docs/compatibility.md) trong cùng PR.

## Thắc mắc

Mở issue tại https://github.com/imrim12/vietscript/issues với label `question`.
Loading
Loading