Skip to content

Commit 08cb92f

Browse files
committed
Merge branch 'main' into freebsd-hw-physmem
2 parents 345ed4d + ef14822 commit 08cb92f

38 files changed

Lines changed: 3949 additions & 111 deletions

‎Makefile.cbm‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ EXTRACTION_SRCS = \
135135
$(CBM_DIR)/extract_k8s.c \
136136
$(CBM_DIR)/helpers.c \
137137
$(CBM_DIR)/lang_specs.c \
138+
$(CBM_DIR)/macro_table.c \
139+
$(CBM_DIR)/iris_export_xml.c \
138140
$(CBM_DIR)/service_patterns.c
139141

140142
# LSP resolvers (compiled as one unit via lsp_all.c)

‎THIRD_PARTY.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ License summary:
4848
original upstream authors' licenses — see the manifest for per-grammar
4949
provenance.
5050

51+
### tree-sitter-objectscript (UDL + routine)
52+
53+
- **Project:** [intersystems/tree-sitter-objectscript](https://github.com/intersystems/tree-sitter-objectscript)
54+
- **License:** MIT
55+
- **Copyright:** (c) 2025 InterSystems Corporation
56+
- **Vendored at:** `internal/cbm/vendored/grammars/objectscript_udl/`, `internal/cbm/vendored/grammars/objectscript_routine/`
57+
- **Pinned commit:** `a7ffcdf`
58+
- **Notes:** InterSystems-maintained grammar for the ObjectScript language (InterSystems IRIS / Caché). Vendor-maintained; not in nvim-treesitter or Helix registries. Each `scanner.c`'s upstream `#include "../../common/scanner.h"` is repointed to a per-directory `objectscript_common.h` (verbatim copy of upstream `common/scanner.h`).
59+
5160
## Vendored C/C++ Libraries
5261

5362
| Library | Path | License | Project |

‎internal/cbm/cbm.c‎

Lines changed: 134 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -721,11 +721,6 @@ static bool cbm_source_nesting_exceeds(const char *source, int source_len, int c
721721
return false;
722722
}
723723

724-
static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
725-
CBMLanguage language, const char *project,
726-
const char *rel_path, int64_t timeout_micros,
727-
const char **extra_defines, const char **include_paths);
728-
729724
/* Best-effort parse-coverage collection (#963). Walks only the has_error paths
730725
* of the tree and records the 1-based line ranges of the TOP-MOST ERROR/MISSING
731726
* nodes (does not descend into an error subtree — one range per failed region).
@@ -849,6 +844,106 @@ static bool cbm_line_contains(const char *src, int src_len, uint32_t line, const
849844
return false;
850845
}
851846

847+
static bool cbm_identifier_char(char c) {
848+
return isalnum((unsigned char)c) || c == '_';
849+
}
850+
851+
/* Verify that the mapped original span contains callable-definition syntax,
852+
* not merely the name at a macro invocation or call site. */
853+
static bool cbm_span_contains_callable_def(const char *src, int src_len, uint32_t start_line,
854+
uint32_t end_line, const char *name) {
855+
if (!src || src_len <= 0 || !name || !name[0] || start_line == 0 || end_line < start_line) {
856+
return false;
857+
}
858+
int span_start = 0;
859+
uint32_t line = 1;
860+
while (span_start < src_len && line < start_line) {
861+
if (src[span_start++] == '\n') {
862+
line++;
863+
}
864+
}
865+
if (line != start_line) {
866+
return false;
867+
}
868+
int span_end = span_start;
869+
while (span_end < src_len && line <= end_line) {
870+
if (src[span_end++] == '\n') {
871+
line++;
872+
}
873+
}
874+
size_t name_len = strlen(name);
875+
for (int pos = span_start; pos + (int)name_len <= span_end; pos++) {
876+
if (strncmp(src + pos, name, name_len) != 0 ||
877+
(pos > 0 && cbm_identifier_char(src[pos - 1])) ||
878+
(pos + (int)name_len < src_len && cbm_identifier_char(src[pos + name_len]))) {
879+
continue;
880+
}
881+
int before = pos;
882+
while (before > span_start && isspace((unsigned char)src[before - 1])) {
883+
before--;
884+
}
885+
if (before > span_start && strchr("(,=!?[.", src[before - 1])) {
886+
continue;
887+
}
888+
int open = pos + (int)name_len;
889+
while (open < span_end && isspace((unsigned char)src[open])) {
890+
open++;
891+
}
892+
if (open >= span_end || src[open] != '(') {
893+
continue;
894+
}
895+
int depth = 0;
896+
int close = -1;
897+
for (int i = open; i < span_end; i++) {
898+
if (src[i] == '(') {
899+
depth++;
900+
} else if (src[i] == ')' && --depth == 0) {
901+
close = i;
902+
break;
903+
}
904+
}
905+
if (close < 0) {
906+
continue;
907+
}
908+
for (int i = close + 1; i < span_end; i++) {
909+
if (src[i] == '{') {
910+
return true;
911+
}
912+
if (src[i] == ';') {
913+
break;
914+
}
915+
}
916+
}
917+
return false;
918+
}
919+
920+
/* Remap an expanded-source definition back to the original input file. Every
921+
* line in the definition must be attributable to the main file; generated
922+
* macro bodies, included headers, and ambiguous spans fail closed. */
923+
static bool cbm_remap_preprocessed_def(CBMDefinition *def, const CBMPreprocessedSource *pp) {
924+
if (!def || !pp || !pp->original_line_by_expanded_line || !pp->belongs_to_main_file ||
925+
def->start_line == 0 || def->end_line < def->start_line ||
926+
def->end_line > (uint32_t)pp->expanded_line_count) {
927+
return false;
928+
}
929+
930+
uint32_t original_start = pp->original_line_by_expanded_line[def->start_line];
931+
uint32_t original_end = pp->original_line_by_expanded_line[def->end_line];
932+
if (!original_start || !original_end || original_end < original_start) {
933+
return false;
934+
}
935+
for (uint32_t line = def->start_line; line <= def->end_line; line++) {
936+
if (!pp->belongs_to_main_file[line] || !pp->original_line_by_expanded_line[line]) {
937+
return false;
938+
}
939+
}
940+
941+
def->start_line = original_start;
942+
def->end_line = original_end;
943+
def->lines = (int)(original_end - original_start + 1);
944+
return true;
945+
}
946+
852947
static void cbm_subtract_recovered_regions(cbm_error_regions_t *regs, const CBMDefArray *defs) {
853948
int kept = 0;
854949
for (int i = 0; i < regs->count; i++) {
@@ -886,17 +981,17 @@ static const char *cbm_error_ranges_str(CBMArena *a, const cbm_error_regions_t *
886981
CBMFileResult *cbm_extract_file(const char *source, int source_len, CBMLanguage language,
887982
const char *project, const char *rel_path, int64_t timeout_micros,
888983
const char **extra_defines, const char **include_paths) {
889-
CBMFileResult *r = cbm_extract_file_impl(source, source_len, language, project, rel_path,
890-
timeout_micros, extra_defines, include_paths);
891-
cbm_index_mark_done(rel_path);
984+
CBMFileResult *r =
985+
cbm_extract_file_ex(source, source_len, language, project, rel_path, timeout_micros,
986+
extra_defines, include_paths, NULL, NULL);
892987
return r;
893988
}
894989

895-
static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
896-
CBMLanguage language, const char *project,
897-
const char *rel_path, int64_t timeout_micros,
898-
const char **extra_defines,
899-
const char **include_paths) {
990+
CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLanguage language,
991+
const char *project, const char *rel_path,
992+
int64_t timeout_micros, const char **extra_defines,
993+
const char **include_paths, const CBMMacroTable *macro_table,
994+
const CBMReturnTypeTable *return_type_table) {
900995
// Allocate result on heap (arena inside for all string data)
901996
enum { SINGLE = 1 };
902997
CBMFileResult *result = (CBMFileResult *)calloc(SINGLE, sizeof(CBMFileResult));
@@ -926,6 +1021,7 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
9261021
if (!spec) {
9271022
result->has_error = true;
9281023
result->error_msg = cbm_arena_strdup(a, "unsupported language");
1024+
cbm_index_mark_done(rel_path);
9291025
return result;
9301026
}
9311027

@@ -934,6 +1030,7 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
9341030
if (!ts_lang) {
9351031
result->has_error = true;
9361032
result->error_msg = cbm_arena_strdup(a, "no tree-sitter grammar");
1033+
cbm_index_mark_done(rel_path);
9371034
return result;
9381035
}
9391036

@@ -953,6 +1050,7 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
9531050
if (!parser) {
9541051
result->has_error = true;
9551052
result->error_msg = cbm_arena_strdup(a, "parser alloc failed");
1053+
cbm_index_mark_done(rel_path);
9561054
return result;
9571055
}
9581056

@@ -985,6 +1083,7 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
9851083
result->has_error = true;
9861084
result->error_msg =
9871085
cbm_arena_strdup(a, timeout_micros > 0 ? "parse timeout" : "parse failed");
1086+
cbm_index_mark_done(rel_path);
9881087
return result;
9891088
}
9901089

@@ -1009,6 +1108,8 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
10091108
.rel_path = rel_path,
10101109
.module_qn = result->module_qn,
10111110
.root = root,
1111+
.macro_table = macro_table,
1112+
.return_type_table = return_type_table,
10121113
};
10131114

10141115
// Run extractors: defs + imports use separate walks (unique recursion patterns),
@@ -1088,9 +1189,10 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
10881189
// Defs keep original-source line numbers; only CALLS are extracted from expanded source.
10891190
if (language == CBM_LANG_C || language == CBM_LANG_CPP || language == CBM_LANG_CUDA) {
10901191
uint64_t pp_start = now_ns();
1091-
char *expanded = cbm_preprocess(source, source_len, rel_path, extra_defines, include_paths,
1092-
language != CBM_LANG_C);
1093-
if (expanded) {
1192+
CBMPreprocessedSource *preprocessed = cbm_preprocess_with_map(
1193+
source, source_len, rel_path, extra_defines, include_paths, language != CBM_LANG_C);
1194+
if (preprocessed && preprocessed->source) {
1195+
char *expanded = preprocessed->source;
10941196
int expanded_len = (int)strlen(expanded);
10951197
// Record calls count before second pass
10961198
int calls_before = result->calls.count;
@@ -1155,15 +1257,20 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
11551257
for (int i = defs_before; i < result->defs.count; i++) {
11561258
CBMDefinition *d = &result->defs.items[i];
11571259
bool adopt = false;
1158-
for (int rj = 0; rj < raw_regs.count && !adopt; rj++) {
1159-
if (d->start_line <= raw_regs.ends[rj] &&
1160-
d->end_line >= raw_regs.starts[rj]) {
1161-
adopt = true;
1260+
if (cbm_remap_preprocessed_def(d, preprocessed)) {
1261+
for (int rj = 0; rj < raw_regs.count && !adopt; rj++) {
1262+
if (d->start_line <= raw_regs.ends[rj] &&
1263+
d->end_line >= raw_regs.starts[rj]) {
1264+
adopt = true;
1265+
}
11621266
}
11631267
}
1164-
if (adopt &&
1165-
(!d->name || !cbm_line_contains(source, source_len,
1166-
d->start_line, d->name))) {
1268+
if (adopt && (!d->name ||
1269+
!cbm_line_contains(source, source_len, d->start_line,
1270+
d->name) ||
1271+
!cbm_span_contains_callable_def(
1272+
source, source_len, d->start_line, d->end_line,
1273+
d->name))) {
11671274
adopt = false;
11681275
}
11691276
for (int j = 0; j < defs_before && adopt; j++) {
@@ -1184,9 +1291,11 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
11841291
ts_tree_delete(pp_tree);
11851292
}
11861293
}
1187-
cbm_preprocess_free(expanded);
1294+
cbm_preprocessed_source_free(preprocessed);
11881295
atomic_fetch_add(&total_files_preprocessed, 1);
11891296
(void)calls_before; // used for future logging
1297+
} else {
1298+
cbm_preprocessed_source_free(preprocessed);
11901299
}
11911300
atomic_fetch_add(&total_preprocess_ns, now_ns() - pp_start);
11921301
}
@@ -1318,6 +1427,7 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len,
13181427
// Retain tree for cross-file LSP reuse (caller frees via cbm_free_tree)
13191428
result->cached_tree = tree;
13201429
result->cached_lang = language;
1430+
cbm_index_mark_done(rel_path);
13211431
return result;
13221432
}
13231433

‎internal/cbm/cbm.h‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,16 @@ typedef enum {
164164
CBM_LANG_APEX,
165165
CBM_LANG_SOQL,
166166
CBM_LANG_SOSL,
167-
CBM_LANG_KUSTOMIZE, // kustomization.yaml — Kubernetes overlay tool
168-
CBM_LANG_K8S, // Generic Kubernetes manifest (apiVersion: detected)
169-
CBM_LANG_PINE, // Pine Script (TradingView indicator / strategy language)
170-
CBM_LANG_QML, // Qt QML (Qt Modeling Language — declarative UI + embedded JS)
171-
CBM_LANG_CFSCRIPT, // CFML script dialect (.cfc components — Lucee/ColdFusion)
172-
CBM_LANG_CFML, // CFML tag dialect (.cfm templates — Lucee/ColdFusion)
173-
CBM_LANG_MOJO, // Mojo
167+
CBM_LANG_KUSTOMIZE, // kustomization.yaml — Kubernetes overlay tool
168+
CBM_LANG_K8S, // Generic Kubernetes manifest (apiVersion: detected)
169+
CBM_LANG_PINE, // Pine Script (TradingView indicator / strategy language)
170+
CBM_LANG_QML, // Qt QML (Qt Modeling Language — declarative UI + embedded JS)
171+
CBM_LANG_CFSCRIPT, // CFML script dialect (.cfc components — Lucee/ColdFusion)
172+
CBM_LANG_CFML, // CFML tag dialect (.cfm templates — Lucee/ColdFusion)
173+
CBM_LANG_MOJO, // Mojo
174+
CBM_LANG_OBJECTSCRIPT_UDL, // InterSystems ObjectScript UDL (.cls class files)
175+
CBM_LANG_OBJECTSCRIPT_ROUTINE, // InterSystems ObjectScript routine (.mac/.int/.rtn/.inc)
176+
CBM_LANG_OBJECTSCRIPT_EXPORT, // InterSystems Studio Export XML (<Export generator="Cache">)
174177
CBM_LANG_COUNT
175178
} CBMLanguage;
176179

@@ -499,6 +502,24 @@ typedef struct {
499502
int count;
500503
} CBMStringConstantMap;
501504

505+
// Forward declaration: ObjectScript macro table (defined in macro_table.h).
506+
typedef struct CBMMacroTable CBMMacroTable;
507+
508+
// Method-return-type table for ObjectScript variable type inference. Populated
509+
// from definition nodes (method QN -> declared return type) so a later
510+
// `Set x = obj.Method()` can resolve x's class.
511+
#define CBM_RETURN_TYPE_TABLE_CAP 2048
512+
513+
typedef struct {
514+
const char *method_qn;
515+
const char *return_type;
516+
} CBMReturnTypeEntry;
517+
518+
typedef struct {
519+
CBMReturnTypeEntry entries[CBM_RETURN_TYPE_TABLE_CAP];
520+
int count;
521+
} CBMReturnTypeTable;
522+
502523
typedef struct {
503524
CBMArena *arena;
504525
CBMFileResult *result;
@@ -509,9 +530,11 @@ typedef struct {
509530
const char *rel_path;
510531
const char *module_qn;
511532
TSNode root;
512-
EFCache ef_cache; // enclosing function cache
513-
const char *enclosing_class_qn; // for nested class QN computation
514-
CBMStringConstantMap string_constants; // module-level NAME = "value" pairs
533+
EFCache ef_cache; // enclosing function cache
534+
const char *enclosing_class_qn; // for nested class QN computation
535+
CBMStringConstantMap string_constants; // module-level NAME = "value" pairs
536+
const CBMMacroTable *macro_table; // ObjectScript $$$macro table (NULL if none)
537+
const CBMReturnTypeTable *return_type_table; // ObjectScript method return types (NULL if none)
515538
} CBMExtractCtx;
516539

517540
// --- Public API ---
@@ -561,6 +584,18 @@ CBMFileResult *cbm_extract_file(const char *source, int source_len, CBMLanguage
561584
const char **include_paths // NULL-terminated, or NULL
562585
);
563586

587+
// Pipeline-internal variant of cbm_extract_file() carrying ObjectScript
588+
// per-project tables (macro table + method-return-type table). The public
589+
// cbm_extract_file() is a thin wrapper that passes NULL, NULL for both.
590+
CBMFileResult *cbm_extract_file_ex(
591+
const char *source, int source_len, CBMLanguage language, const char *project,
592+
const char *rel_path, int64_t timeout_micros,
593+
const char **extra_defines, // NULL-terminated, or NULL
594+
const char **include_paths, // NULL-terminated, or NULL
595+
const CBMMacroTable *macro_table, // ObjectScript macros, or NULL
596+
const CBMReturnTypeTable *return_type_table // OS return types, or NULL
597+
);
598+
564599
// Free all memory associated with a result.
565600
void cbm_free_result(CBMFileResult *result);
566601

0 commit comments

Comments
 (0)