Skip to content

Commit 61857fd

Browse files
authored
Merge pull request DeusData#1842 from DeusData/feat/arkts-language
feat(lang): ArkTS (.ets) support via a first-party tree-sitter-typescript fork
2 parents 909051c + 2b51b7d commit 61857fd

53 files changed

Lines changed: 598228 additions & 48 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎THIRD_PARTY.md‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The core runtime headers in `internal/cbm/vendored/common/tree_sitter/`
3737

3838
## Tree-sitter Grammars
3939

40-
159 pre-generated parsers are vendored in `internal/cbm/vendored/grammars/<lang>/`
40+
160 pre-generated parsers are vendored in `internal/cbm/vendored/grammars/<lang>/`
4141
(generated `parser.c` plus `scanner.c` where applicable, compiled statically).
4242
Each grammar is the work of its upstream authors and each grammar directory
4343
contains the upstream `LICENSE` file.
@@ -54,10 +54,15 @@ License summary:
5454
`pine` is **ISC** (declared by its upstream).
5555
- The grammars authored in-house for this project (`cobol`, `form`, `janet`,
5656
`magma`, `protobuf`, `wolfram`) are **MIT** under the project's own license,
57-
(c) DeusData. Six further grammars (`assembly`, `cfml`, `cfscript`,
58-
`dotenv`, `pine`, `qml`) are self-maintained forks that retain their
59-
original upstream authors' licenses — see the manifest for per-grammar
60-
provenance.
57+
(c) DeusData. Seven further grammars (`arkts`, `assembly`, `cfml`,
58+
`cfscript`, `dotenv`, `pine`, `qml`) are self-maintained forks that retain
59+
their original upstream authors' licenses — see the manifest for per-grammar
60+
provenance. `arkts` is a first-party derivative of
61+
[tree-sitter/tree-sitter-typescript](https://github.com/tree-sitter/tree-sitter-typescript)
62+
(MIT, (c) 2017 Max Brunsfeld; on the
63+
[tree-sitter-javascript](https://github.com/tree-sitter/tree-sitter-javascript)
64+
base, MIT, (c) 2014 Max Brunsfeld) with (c) 2026 DeusData ArkTS additions;
65+
its grammar source lives in `tools/tree-sitter-arkts/`.
6166

6267
### tree-sitter-objectscript (UDL + routine)
6368

‎internal/cbm/cbm.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ typedef enum {
174174
CBM_LANG_OBJECTSCRIPT_UDL, // InterSystems ObjectScript UDL (.cls class files)
175175
CBM_LANG_OBJECTSCRIPT_ROUTINE, // InterSystems ObjectScript routine (.mac/.int/.rtn/.inc)
176176
CBM_LANG_OBJECTSCRIPT_EXPORT, // InterSystems Studio Export XML (<Export generator="Cache">)
177+
CBM_LANG_ARKTS, // ArkTS (HarmonyOS/OpenHarmony .ets — TypeScript superset + ArkUI)
177178
CBM_LANG_COUNT
178179
} CBMLanguage;
179180

‎internal/cbm/extract_calls.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3338,7 +3338,7 @@ CBMInvocationDescriptor handle_calls(CBMExtractCtx *ctx, TSNode node, const CBML
33383338
// right. Bare calls (helper()) and new_expression have no member
33393339
// receiver, so they keep is_method=false (struct is zero-init).
33403340
if ((ctx->language == CBM_LANG_JAVASCRIPT || ctx->language == CBM_LANG_TYPESCRIPT ||
3341-
ctx->language == CBM_LANG_TSX) &&
3341+
ctx->language == CBM_LANG_TSX || ctx->language == CBM_LANG_ARKTS) &&
33423342
strcmp(ts_node_type(node), "call_expression") == 0) {
33433343
TSNode fn = ts_node_child_by_field_name(node, TS_FIELD("function"));
33443344
if (!ts_node_is_null(fn) && strcmp(ts_node_type(fn), "member_expression") == 0) {

‎internal/cbm/extract_channels.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,7 @@ void cbm_extract_channels(CBMExtractCtx *ctx) {
10581058
case CBM_LANG_JAVASCRIPT:
10591059
case CBM_LANG_TYPESCRIPT:
10601060
case CBM_LANG_TSX:
1061+
case CBM_LANG_ARKTS:
10611062
extract_channels_js(ctx);
10621063
break;
10631064
case CBM_LANG_PYTHON:

‎internal/cbm/extract_defs.c‎

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2550,7 +2550,7 @@ static const char **extract_base_classes(CBMArena *a, TSNode node, const char *s
25502550
}
25512551
// Languages whose heritage is not exposed via a tree-sitter field need
25522552
// dedicated walkers; the generic field/keyword path mis-captures them.
2553-
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX) {
2553+
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX || lang == CBM_LANG_ARKTS) {
25542554
const char **ts_result = extract_ts_bases(a, node, source);
25552555
if (ts_result) {
25562556
return ts_result;
@@ -3097,7 +3097,7 @@ static char *resolve_param_type_text(CBMArena *a, TSNode param, const char *sour
30973097
return ts_node_is_null(typename) ? NULL : cbm_node_text(a, typename, source);
30983098
}
30993099

3100-
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX) {
3100+
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX || lang == CBM_LANG_ARKTS) {
31013101
if (strcmp(pk, "required_parameter") == 0 || strcmp(pk, "optional_parameter") == 0) {
31023102
return extract_ts_param_type(a, param, source);
31033103
}
@@ -3230,7 +3230,7 @@ static bool is_signature_implicit_receiver(CBMArena *a, TSNode param, const char
32303230
return true;
32313231
}
32323232

3233-
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX) {
3233+
if (lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX || lang == CBM_LANG_ARKTS) {
32343234
char *name = signature_receiver_name(a, param, source, false);
32353235
return name && strcmp(name, "this") == 0;
32363236
}
@@ -3658,7 +3658,7 @@ static void extract_func_def(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
36583658
if (ctx->enclosing_class_qn &&
36593659
(ctx->language == CBM_LANG_CPP || ctx->language == CBM_LANG_CUDA ||
36603660
ctx->language == CBM_LANG_TYPESCRIPT || ctx->language == CBM_LANG_TSX ||
3661-
ctx->language == CBM_LANG_NIX)) {
3661+
ctx->language == CBM_LANG_ARKTS || ctx->language == CBM_LANG_NIX)) {
36623662
def.qualified_name = cbm_arena_sprintf(a, "%s.%s", ctx->enclosing_class_qn, qn_name);
36633663
}
36643664
def.label = "Function";
@@ -3792,7 +3792,7 @@ static void extract_func_def(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
37923792

37933793
// JS/TS export detection
37943794
if (ctx->language == CBM_LANG_JAVASCRIPT || ctx->language == CBM_LANG_TYPESCRIPT ||
3795-
ctx->language == CBM_LANG_TSX) {
3795+
ctx->language == CBM_LANG_TSX || ctx->language == CBM_LANG_ARKTS) {
37963796
if (is_js_exported(node)) {
37973797
def.is_entry_point = true;
37983798
}
@@ -4395,16 +4395,17 @@ static void extract_class_def(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
43954395
label = "Interface";
43964396
}
43974397
}
4398-
// Rust/Swift/D: a struct is a distinct kind from a class — emit the precise
4399-
// "Struct" label rather than collapsing it to "Class". Scoped to these three
4400-
// grammar/LSP languages. Rust's struct node is `struct_item`; D's is
4401-
// `struct_declaration`. C/C++/Obj-C keep `struct_specifier` → "Class"
4402-
// (a C++ struct is class-like). "Struct" is a type-like container: every
4403-
// type-resolution / registry / IMPLEMENTS / LSP-registrar consumer routes
4404-
// through cbm_label_is_type_like(), so a struct still resolves as a type for
4405-
// its methods, fields, inheritance and impls.
4398+
// Rust/Swift/D/ArkTS: a struct is a distinct kind from a class — emit the
4399+
// precise "Struct" label rather than collapsing it to "Class". Scoped to
4400+
// these grammar/LSP languages. Rust's struct node is `struct_item`; D's and
4401+
// ArkTS's is `struct_declaration` (an ArkUI @Component custom component).
4402+
// C/C++/Obj-C keep `struct_specifier` → "Class" (a C++ struct is
4403+
// class-like). "Struct" is a type-like container: every type-resolution /
4404+
// registry / IMPLEMENTS / LSP-registrar consumer routes through
4405+
// cbm_label_is_type_like(), so a struct still resolves as a type for its
4406+
// methods, fields, inheritance and impls.
44064407
if (ctx->language == CBM_LANG_RUST || ctx->language == CBM_LANG_SWIFT ||
4407-
ctx->language == CBM_LANG_DLANG) {
4408+
ctx->language == CBM_LANG_DLANG || ctx->language == CBM_LANG_ARKTS) {
44084409
if (strcmp(kind, "struct_item") == 0 || strcmp(kind, "struct_declaration") == 0) {
44094410
label = "Struct";
44104411
}
@@ -5492,6 +5493,7 @@ static void extract_vars_mainstream(CBMExtractCtx *ctx, TSNode node, CBMArena *a
54925493
case CBM_LANG_JAVASCRIPT:
54935494
case CBM_LANG_TYPESCRIPT:
54945495
case CBM_LANG_TSX:
5496+
case CBM_LANG_ARKTS:
54955497
extract_js_vars(ctx, node, a);
54965498
break;
54975499
case CBM_LANG_JAVA: {
@@ -5988,6 +5990,7 @@ static void extract_var_names(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
59885990
case CBM_LANG_JAVASCRIPT:
59895991
case CBM_LANG_TYPESCRIPT:
59905992
case CBM_LANG_TSX:
5993+
case CBM_LANG_ARKTS:
59915994
case CBM_LANG_JAVA:
59925995
case CBM_LANG_CSHARP:
59935996
case CBM_LANG_CPP:
@@ -6683,6 +6686,10 @@ static void extract_class_fields(CBMExtractCtx *ctx, TSNode class_node, const ch
66836686
def.start_line = ts_node_start_point(child).row + TS_LINE_OFFSET;
66846687
def.end_line = ts_node_end_point(child).row + TS_LINE_OFFSET;
66856688
def.is_exported = cbm_is_exported(name, ctx->language);
6689+
/* Field decorators/annotations (ArkTS @State/@Prop/@Link state members,
6690+
* TS @Input()-style fields, JVM field annotations via the modifiers
6691+
* child) — same extraction the class and method paths already run. */
6692+
def.decorators = extract_decorators(a, child, ctx->source, ctx->language, spec);
66866693

66876694
cbm_defs_push(&ctx->result->defs, a, def);
66886695
}
@@ -7481,7 +7488,8 @@ static void walk_defs(CBMExtractCtx *ctx, TSNode root, const CBMLangSpec *spec,
74817488
bool descend_into_func =
74827489
(ctx->language == CBM_LANG_WOLFRAM || ctx->language == CBM_LANG_TYPESCRIPT ||
74837490
ctx->language == CBM_LANG_JAVASCRIPT || ctx->language == CBM_LANG_TSX ||
7484-
ctx->language == CBM_LANG_ADA || ctx->language == CBM_LANG_NIX);
7491+
ctx->language == CBM_LANG_ARKTS || ctx->language == CBM_LANG_ADA ||
7492+
ctx->language == CBM_LANG_NIX);
74857493
if (!descend_into_func) {
74867494
continue;
74877495
}
@@ -7501,7 +7509,8 @@ static void walk_defs(CBMExtractCtx *ctx, TSNode root, const CBMLangSpec *spec,
75017509
* the class/func paths on the namespace node itself. */
75027510
if (is_namespace_scope_kind(ctx->language, kind, node)) {
75037511
const char *new_enclosing = compute_class_qn(ctx, node, frame.enclosing_class_qn);
7504-
if (ctx->language == CBM_LANG_TYPESCRIPT || ctx->language == CBM_LANG_TSX) {
7512+
if (ctx->language == CBM_LANG_TYPESCRIPT || ctx->language == CBM_LANG_TSX ||
7513+
ctx->language == CBM_LANG_ARKTS) {
75057514
extract_typescript_namespace_def(ctx, node, frame.enclosing_class_qn);
75067515
}
75077516
wd_push_children_reverse(&s, node, new_enclosing);

‎internal/cbm/extract_imports.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,6 +2764,7 @@ void cbm_extract_imports(CBMExtractCtx *ctx) {
27642764
case CBM_LANG_JAVASCRIPT:
27652765
case CBM_LANG_TYPESCRIPT:
27662766
case CBM_LANG_TSX:
2767+
case CBM_LANG_ARKTS:
27672768
parse_es_imports(ctx);
27682769
break;
27692770
case CBM_LANG_JAVA:

‎internal/cbm/extract_type_refs.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ static void process_body_type_ref(CBMExtractCtx *ctx, TSNode node, const char *f
219219
break;
220220
case CBM_LANG_TYPESCRIPT:
221221
case CBM_LANG_TSX:
222+
case CBM_LANG_ARKTS:
222223
extract_ts_body_type_refs(ctx, node, kind, func_qn);
223224
break;
224225
case CBM_LANG_JAVA:

‎internal/cbm/extract_unified.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,6 +1870,7 @@ static bool is_actual_import_boundary(CBMExtractCtx *ctx, TSNode node, const CBM
18701870
case CBM_LANG_JAVASCRIPT:
18711871
case CBM_LANG_TYPESCRIPT:
18721872
case CBM_LANG_TSX:
1873+
case CBM_LANG_ARKTS:
18731874
if (strcmp(kind, "export_statement") == 0) {
18741875
/* An export is an import CONTEXT only in its re-export forms:
18751876
* `export ... from 'mod'` (source field) or a bare specifier list

‎internal/cbm/extract_usages.c‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ static bool is_reference_node(TSNode node, CBMLanguage lang) {
104104
case CBM_LANG_JAVASCRIPT:
105105
case CBM_LANG_TYPESCRIPT:
106106
case CBM_LANG_TSX:
107+
case CBM_LANG_ARKTS:
107108
case CBM_LANG_QML:
108109
case CBM_LANG_CFSCRIPT:
109110
return strcmp(kind, "property_identifier") == 0 ||
@@ -1367,6 +1368,7 @@ static bool language_may_stamp_exact_callable_value_candidate(CBMLanguage langua
13671368
case CBM_LANG_JAVASCRIPT:
13681369
case CBM_LANG_TYPESCRIPT:
13691370
case CBM_LANG_TSX:
1371+
case CBM_LANG_ARKTS:
13701372
case CBM_LANG_GO:
13711373
case CBM_LANG_PYTHON:
13721374
case CBM_LANG_C:
@@ -1492,7 +1494,7 @@ static TSNode call_reference_candidate_site(CBMExtractCtx *ctx, TSNode node, con
14921494
(void)occurrence_parent(cursor, node, &parent, &parent_field);
14931495
}
14941496
bool ts_family = ctx->language == CBM_LANG_JAVASCRIPT || ctx->language == CBM_LANG_TYPESCRIPT ||
1495-
ctx->language == CBM_LANG_TSX;
1497+
ctx->language == CBM_LANG_TSX || ctx->language == CBM_LANG_ARKTS;
14961498
if (ts_family && strcmp(kind, "property_identifier") == 0 && !ts_node_is_null(parent) &&
14971499
strcmp(ts_node_type(parent), "member_expression") == 0) {
14981500
TSNode property = ts_node_child_by_field_name(parent, TS_FIELD("property"));
@@ -1995,7 +1997,8 @@ static bool is_import_binding_occurrence(CBMExtractCtx *ctx, TSNode node, const
19951997
}
19961998
case CBM_LANG_JAVASCRIPT:
19971999
case CBM_LANG_TYPESCRIPT:
1998-
case CBM_LANG_TSX: {
2000+
case CBM_LANG_TSX:
2001+
case CBM_LANG_ARKTS: {
19992002
if (strcmp(ts_node_type(boundary), "import_statement") != 0) {
20002003
return false;
20012004
}
@@ -2182,7 +2185,7 @@ static void record_lexical_binding(CBMExtractCtx *ctx, WalkState *state, TSNode
21822185
scope_id = lexical_ancestor_of_kind(state, current_id, true, false);
21832186
whole_scope = true;
21842187
} else if (ctx->language == CBM_LANG_JAVASCRIPT || ctx->language == CBM_LANG_TYPESCRIPT ||
2185-
ctx->language == CBM_LANG_TSX) {
2188+
ctx->language == CBM_LANG_TSX || ctx->language == CBM_LANG_ARKTS) {
21862189
bool is_var = js_var_binding(node);
21872190
scope_id = lexical_ancestor_of_kind(state, current_id, is_var, !is_var);
21882191
if (scope_id == 0) {

‎internal/cbm/grammar_arkts.c‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Vendored tree-sitter grammar: arkts (first-party fork of tree-sitter-typescript)
2+
// Each grammar compiled as separate unit (conflicting static symbols).
3+
#include "vendored/grammars/arkts/parser.c"
4+
#include "vendored/grammars/arkts/scanner.c"

0 commit comments

Comments
 (0)