Skip to content

Commit 5ee8198

Browse files
committed
chore: Expose async API for parsing C++ code
Signed-off-by: Roberto Raggi <[email protected]>
1 parent d0ae3e4 commit 5ee8198

File tree

12 files changed

+217
-138
lines changed

12 files changed

+217
-138
lines changed

packages/cxx-frontend/src/Parser.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import initCxx, { cxx } from "./cxx";
2222
import { Unit } from "./Unit";
2323
import { AST } from "./AST";
2424

25-
interface ParseParams {
25+
interface ParserParams {
2626
/**
2727
* Path to the file to parse.
2828
*/
@@ -32,6 +32,12 @@ interface ParseParams {
3232
* Source code to parse.
3333
*/
3434
source: string;
35+
36+
resolve?: (
37+
name: string,
38+
kind: "quoted" | "angled",
39+
next: boolean,
40+
) => Promise<string | undefined>;
3541
}
3642

3743
export class Parser {
@@ -69,8 +75,8 @@ export class Parser {
6975
return cxx !== undefined && cxx !== null;
7076
}
7177

72-
constructor(options: ParseParams) {
73-
const { path, source } = options;
78+
constructor(options: ParserParams) {
79+
const { path, source, resolve } = options;
7480

7581
if (typeof path !== "string") {
7682
throw new TypeError("expected parameter 'path' of type 'string'");
@@ -80,14 +86,14 @@ export class Parser {
8086
throw new TypeError("expected parameter 'source' of type 'string'");
8187
}
8288

83-
this.#unit = cxx.createUnit(source, path);
89+
this.#unit = cxx.createUnit(source, path, { resolve });
8490
}
8591

86-
parse() {
92+
async parse() {
8793
if (!this.#unit) {
8894
return;
8995
}
90-
this.#unit.parse();
96+
await this.#unit.parse();
9197
this.#ast = AST.from(this.#unit.getHandle(), this);
9298
}
9399

packages/cxx-frontend/src/Preprocessor.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ import { cxx } from "./cxx";
2222

2323
interface PreprocessorOptions {
2424
systemIncludePaths?: string[];
25-
26-
fs?: {
27-
existsSync(path: string): boolean;
28-
readFileSync(path: string): string;
29-
};
3025
}
3126

3227
export class Preprocessor {
@@ -39,7 +34,7 @@ export class Preprocessor {
3934
*
4035
* @param source
4136
*/
42-
constructor({ systemIncludePaths, fs }: PreprocessorOptions = {}) {
37+
constructor({ systemIncludePaths }: PreprocessorOptions = {}) {
4338
this.#control = new cxx.Control();
4439
this.#diagnosticClient = new cxx.DiagnosticsClient();
4540
this.#handle = new cxx.Preprocessor(this.#control, this.#diagnosticClient);
@@ -48,13 +43,6 @@ export class Preprocessor {
4843
systemIncludePaths?.forEach((path) => {
4944
this.#handle.addIncludePath(path);
5045
});
51-
52-
if (fs) {
53-
const { existsSync, readFileSync } = fs;
54-
55-
this.#handle.setCanResolveFiles(true);
56-
this.#handle.setup(existsSync, readFileSync);
57-
}
5846
}
5947

6048
/**

packages/cxx-frontend/src/Unit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { Diagnostic } from "./Diagnostic";
2222

2323
export interface Unit {
2424
delete(): void;
25-
parse(): boolean;
25+
parse(): Promise<boolean>;
2626
getHandle(): number;
2727
getUnitHandle(): number;
2828
getDiagnostics(): Diagnostic[];

packages/cxx-frontend/src/cxx-js.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,22 @@ interface Lexer {
8383
tokenText(): string;
8484
}
8585

86+
interface Api {
87+
resolve?: (
88+
name: string,
89+
kind: "quoted" | "angled",
90+
isIncludeNext: boolean,
91+
) => Promise<string | undefined>;
92+
}
93+
8694
export type CXX = {
8795
Control: Control;
8896
DiagnosticsClient: DiagnosticsClient;
8997
Preprocessor: Preprocessor;
9098
Lexer: Lexer;
9199
TranslationUnit: TranslationUnit;
92100

93-
createUnit(source: string, path: string): Unit;
101+
createUnit(source: string, path: string, api: Api): Unit;
94102
getASTKind(handle: number): number;
95103
getASTSlot(handle: number, slot: number): number;
96104
getASTSlotKind(handle: number, slot: number): ASTSlotKind;

src/js/cxx/api.cc

Lines changed: 104 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <emscripten/val.h>
3333

3434
#include <format>
35+
#include <iostream>
3536
#include <sstream>
3637

3738
using namespace emscripten;
@@ -61,42 +62,121 @@ struct DiagnosticsClient final : cxx::DiagnosticsClient {
6162
struct WrappedUnit {
6263
std::unique_ptr<DiagnosticsClient> diagnosticsClient;
6364
std::unique_ptr<cxx::TranslationUnit> unit;
65+
val api;
6466

65-
WrappedUnit(std::string source, std::string filename) {
67+
WrappedUnit(std::string source, std::string filename, val api = {})
68+
: api(api) {
6669
diagnosticsClient = std::make_unique<DiagnosticsClient>();
70+
6771
unit = std::make_unique<cxx::TranslationUnit>(diagnosticsClient.get());
72+
6873
if (auto preprocessor = unit->preprocessor()) {
69-
preprocessor->setCanResolveFiles(false);
74+
preprocessor->setCanResolveFiles(true);
7075
}
7176

72-
unit->setSource(std::move(source), std::move(filename));
77+
unit->beginPreprocessing(std::move(source), std::move(filename));
78+
}
79+
80+
auto getUnitHandle() const -> std::intptr_t {
81+
return (std::intptr_t)unit.get();
7382
}
7483

75-
std::intptr_t getUnitHandle() const { return (std::intptr_t)unit.get(); }
84+
auto getHandle() const -> std::intptr_t { return (std::intptr_t)unit->ast(); }
7685

77-
std::intptr_t getHandle() const { return (std::intptr_t)unit->ast(); }
86+
auto getDiagnostics() const -> val { return diagnosticsClient->messages; }
7887

79-
val getDiagnostics() const { return diagnosticsClient->messages; }
88+
auto parse() -> val {
89+
val resolve = val::undefined();
90+
91+
if (!api.isUndefined()) {
92+
resolve = api["resolve"];
93+
}
94+
95+
struct {
96+
auto operator()(const cxx::SystemInclude& include) -> val {
97+
return val(include.fileName);
98+
}
99+
auto operator()(const cxx::QuoteInclude& include) -> val {
100+
return val(include.fileName);
101+
}
102+
} getHeaderName;
103+
104+
struct {
105+
val quoted{"quoted"};
106+
val angled{"angled"};
107+
108+
auto operator()(const cxx::SystemInclude& include) -> val {
109+
return angled;
110+
}
111+
auto operator()(const cxx::QuoteInclude& include) -> val {
112+
return quoted;
113+
}
114+
} getIncludeType;
115+
116+
while (true) {
117+
auto state = unit->continuePreprocessing();
118+
119+
if (std::holds_alternative<cxx::ProcessingComplete>(state)) break;
120+
121+
if (auto pendingInclude = std::get_if<cxx::PendingInclude>(&state)) {
122+
if (resolve.isUndefined()) {
123+
pendingInclude->resolveWith(std::nullopt);
124+
continue;
125+
}
126+
127+
auto header = std::visit(getHeaderName, pendingInclude->include);
128+
auto includeType = std::visit(getIncludeType, pendingInclude->include);
129+
130+
val resolved = co_await resolve(header, includeType,
131+
pendingInclude->isIncludeNext);
132+
133+
if (resolved.isString()) {
134+
pendingInclude->resolveWith(resolved.as<std::string>());
135+
} else {
136+
pendingInclude->resolveWith(std::nullopt);
137+
}
138+
139+
} else if (auto pendingHasIncludes =
140+
std::get_if<cxx::PendingHasIncludes>(&state)) {
141+
for (auto& request : pendingHasIncludes->requests) {
142+
if (resolve.isUndefined()) {
143+
request.setExists(false);
144+
continue;
145+
}
146+
147+
auto header = std::visit(getHeaderName, request.include);
148+
auto includeType = std::visit(getIncludeType, request.include);
149+
150+
val resolved =
151+
co_await resolve(header, includeType, request.isIncludeNext);
152+
153+
request.setExists(resolved.isString());
154+
}
155+
}
156+
}
157+
158+
unit->endPreprocessing();
80159

81-
bool parse() {
82160
unit->parse();
83-
return true;
161+
162+
co_return val{true};
84163
}
85164
};
86165

87-
std::string getTokenText(std::intptr_t handle, std::intptr_t unitHandle) {
166+
auto getTokenText(std::intptr_t handle, std::intptr_t unitHandle)
167+
-> std::string {
88168
auto unit = reinterpret_cast<cxx::TranslationUnit*>(unitHandle);
89169
auto text = unit->tokenText(cxx::SourceLocation(handle));
90170
return text;
91171
}
92172

93-
int getTokenKind(std::intptr_t handle, std::intptr_t unitHandle) {
173+
auto getTokenKind(std::intptr_t handle, std::intptr_t unitHandle) -> int {
94174
auto unit = reinterpret_cast<cxx::TranslationUnit*>(unitHandle);
95175
auto kind = unit->tokenKind(cxx::SourceLocation(handle));
96176
return static_cast<int>(kind);
97177
}
98178

99-
val getTokenLocation(std::intptr_t handle, std::intptr_t unitHandle) {
179+
auto getTokenLocation(std::intptr_t handle, std::intptr_t unitHandle) -> val {
100180
auto unit = reinterpret_cast<cxx::TranslationUnit*>(unitHandle);
101181

102182
cxx::SourceLocation loc(handle);
@@ -114,71 +194,71 @@ val getTokenLocation(std::intptr_t handle, std::intptr_t unitHandle) {
114194
return result;
115195
}
116196

117-
val getStartLocation(std::intptr_t handle, std::intptr_t unitHandle) {
197+
auto getStartLocation(std::intptr_t handle, std::intptr_t unitHandle) -> val {
118198
auto ast = reinterpret_cast<cxx::AST*>(handle);
119199
const auto loc = ast->firstSourceLocation();
120200
if (!loc) return {};
121201
return getTokenLocation(loc.index(), unitHandle);
122202
}
123203

124-
val getEndLocation(std::intptr_t handle, std::intptr_t unitHandle) {
204+
auto getEndLocation(std::intptr_t handle, std::intptr_t unitHandle) -> val {
125205
auto ast = reinterpret_cast<cxx::AST*>(handle);
126206
const auto loc = ast->lastSourceLocation().previous();
127207
if (!loc) return {};
128208
return getTokenLocation(loc.index(), unitHandle);
129209
}
130210

131-
val getIdentifierValue(std::intptr_t handle) {
211+
auto getIdentifierValue(std::intptr_t handle) -> val {
132212
auto id = reinterpret_cast<const cxx::Identifier*>(handle);
133213
if (!id) return {};
134214
return val(id->value());
135215
}
136216

137-
val getLiteralValue(std::intptr_t handle) {
217+
auto getLiteralValue(std::intptr_t handle) -> val {
138218
auto id = reinterpret_cast<const cxx::Literal*>(handle);
139219
if (!id) return {};
140220
return val(id->value());
141221
}
142222

143-
int getASTKind(std::intptr_t handle) {
223+
auto getASTKind(std::intptr_t handle) -> int {
144224
return static_cast<int>(((cxx::AST*)handle)->kind());
145225
}
146226

147-
int getListValue(std::intptr_t handle) {
227+
auto getListValue(std::intptr_t handle) -> int {
148228
auto list = reinterpret_cast<cxx::List<cxx::AST*>*>(handle);
149229
return std::intptr_t(list->value);
150230
}
151231

152-
std::intptr_t getListNext(std::intptr_t handle) {
232+
auto getListNext(std::intptr_t handle) -> std::intptr_t {
153233
auto list = reinterpret_cast<cxx::List<cxx::AST*>*>(handle);
154234
return std::intptr_t(list->next);
155235
}
156236

157-
std::intptr_t getASTSlot(std::intptr_t handle, int slot) {
237+
auto getASTSlot(std::intptr_t handle, int slot) -> std::intptr_t {
158238
auto ast = reinterpret_cast<cxx::AST*>(handle);
159239
auto [value, slotKind, slotNameIndex, slotCount] = getSlot(ast, slot);
160240
return value;
161241
}
162242

163-
int getASTSlotKind(std::intptr_t handle, int slot) {
243+
auto getASTSlotKind(std::intptr_t handle, int slot) -> int {
164244
auto ast = reinterpret_cast<cxx::AST*>(handle);
165245
auto [value, slotKind, slotNameIndex, slotCount] = getSlot(ast, slot);
166246
return static_cast<int>(slotKind);
167247
}
168248

169-
int getASTSlotName(std::intptr_t handle, int slot) {
249+
auto getASTSlotName(std::intptr_t handle, int slot) -> int {
170250
auto ast = reinterpret_cast<cxx::AST*>(handle);
171251
auto [value, slotKind, slotName, slotCount] = getSlot(ast, slot);
172252
return static_cast<int>(slotName);
173253
}
174254

175-
int getASTSlotCount(std::intptr_t handle, int slot) {
255+
auto getASTSlotCount(std::intptr_t handle, int slot) -> int {
176256
auto ast = reinterpret_cast<cxx::AST*>(handle);
177257
auto [value, slotKind, slotNameIndex, slotCount] = getSlot(ast, slot);
178258
return static_cast<int>(slotCount);
179259
}
180260

181-
WrappedUnit* createUnit(std::string source, std::string filename) {
261+
auto createUnit(std::string source, std::string filename) -> WrappedUnit* {
182262
auto wrapped = new WrappedUnit(std::move(source), std::move(filename));
183263

184264
return wrapped;
@@ -196,17 +276,6 @@ auto lexerNext(cxx::Lexer& lexer) -> int {
196276
return static_cast<int>(lexer.next());
197277
}
198278

199-
void preprocessorSetup(cxx::Preprocessor& preprocessor, val fileExistsFn,
200-
val readFileFn) {
201-
preprocessor.setFileExistsFunction([fileExistsFn](std::string fileName) {
202-
return fileExistsFn(fileName).as<bool>();
203-
});
204-
205-
preprocessor.setReadFileFunction([readFileFn](std::string fileName) {
206-
return readFileFn(fileName).as<std::string>();
207-
});
208-
}
209-
210279
auto preprocesorPreprocess(cxx::Preprocessor& preprocessor, std::string source,
211280
std::string filename) -> std::string {
212281
std::vector<cxx::Token> tokens;
@@ -241,7 +310,6 @@ auto register_preprocessor(const char* name = "Preprocessor")
241310
return class_<cxx::Preprocessor>(name)
242311
.constructor<cxx::Control*, cxx::DiagnosticsClient*>()
243312
.function("preprocess", &preprocesorPreprocess)
244-
.function("setup", &preprocessorSetup)
245313
.function("addIncludePath", &cxx::Preprocessor::addSystemIncludePath)
246314
.function("defineMacro", &cxx::Preprocessor::defineMacro)
247315
.function("undefineMacro", &cxx::Preprocessor::undefMacro)
@@ -283,7 +351,7 @@ auto register_translation_unit(const char* name = "TranslationUnit")
283351

284352
} // namespace
285353

286-
EMSCRIPTEN_BINDINGS(my_module) {
354+
EMSCRIPTEN_BINDINGS(cxx) {
287355
register_control();
288356
register_diagnostics_client();
289357
register_preprocessor();

src/parser/cxx/cxx_fwd.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
#pragma once
2222

23-
#include <cstdint>
2423
#include <string>
2524

2625
namespace cxx {

src/parser/cxx/literals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <cxx/cxx_fwd.h>
2222

23+
#include <cstdint>
2324
#include <string>
2425
#include <string_view>
2526

0 commit comments

Comments
 (0)