Skip to content

Commit

Permalink
fix: Expose TranslationUnit to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
robertoraggi committed Jul 3, 2023
1 parent 90e1f8a commit 7792714
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 47 deletions.
16 changes: 7 additions & 9 deletions packages/cxx-frontend/examples/dump.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ async function main() {

const ast = parser.getAST();

if (ast) {
ast.walk().preVisit((node, depth) => {
if (node instanceof AST) {
const ind = " ".repeat(depth * 2);
const kind = ASTKind[node.getKind()];
console.log(`${ind}${kind}`);
}
});
}
ast?.walk().preVisit((node, depth) => {
if (node instanceof AST) {
const ind = " ".repeat(depth * 2);
const kind = ASTKind[node.getKind()];
console.log(`${ind}${kind}`);
}
});

parser.dispose();
}
Expand Down
45 changes: 45 additions & 0 deletions packages/cxx-frontend/examples/unit.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Parser, TokenKind, TranslationUnit, AST, ASTKind } from "../dist/index.js";
import { readFile } from "fs/promises";
import { fileURLToPath } from "url";

const source = `
int main() {
return 0;
}
`;

async function main() {
const wasmBinaryFile = fileURLToPath(Parser.DEFAULT_WASM_BINARY_URL);

const wasmBinary = await readFile(wasmBinaryFile);

// initialize the parser
await Parser.init({ wasmBinary });

const translationUnit = new TranslationUnit();

translationUnit.preprocess(source, "main.cc");

console.log("== Tokens:");

for (const token of translationUnit.tokens()) {
console.log(TokenKind[token.getKind()], token.getText());
}

const ast = translationUnit.parse();

console.log();
console.log("== AST:")

ast?.walk().preVisit((node, depth) => {
if (node instanceof AST) {
const ind = " ".repeat(depth * 2);
const kind = ASTKind[node.getKind()];
console.log(`${ind}${kind}`);
}
});

translationUnit.dispose();
}

main().catch(console.error);
11 changes: 7 additions & 4 deletions packages/cxx-frontend/src/AST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import { SourceLocation } from "./SourceLocation.js";
import { ASTCursor } from "./ASTCursor.js";
import { ASTVisitor } from "./ASTVisitor.js";
import { ASTKind } from "./ASTKind.js";
import { Parser } from "./Parser.js";
import { Token } from "./Token.js";

interface TranslationUnitLike {
getUnitHandle(): number;
}

export abstract class AST {
constructor(private readonly handle: number,
private readonly kind: ASTKind,
protected readonly parser: Parser) {
protected readonly parser: TranslationUnitLike) {
}

walk(): ASTCursor {
Expand Down Expand Up @@ -62,7 +65,7 @@ export abstract class AST {

abstract accept<Context, Result>(visitor: ASTVisitor<Context, Result>, context: Context): Result;

static from<T extends AST = AST>(handle: number, parser: Parser): T | undefined {
static from<T extends AST = AST>(handle: number, parser: TranslationUnitLike): T | undefined {
if (handle) {
const kind = cxx.getASTKind(handle) as ASTKind;
const ast = new AST_CONSTRUCTORS[kind](handle, kind, parser) as T;
Expand Down Expand Up @@ -3204,7 +3207,7 @@ export class SimpleAttributeTokenAST extends AttributeTokenAST {
}
}

const AST_CONSTRUCTORS: Array<new (handle: number, kind: ASTKind, parser: Parser) => AST> = [
const AST_CONSTRUCTORS: Array<new (handle: number, kind: ASTKind, parser: TranslationUnitLike) => AST> = [
TypeIdAST,
NestedNameSpecifierAST,
UsingDeclaratorAST,
Expand Down
12 changes: 8 additions & 4 deletions packages/cxx-frontend/src/ASTCursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ import { Token } from "./Token.js";
import { ASTSlotKind, cxx } from "./cxx.js";
import { Parser } from "./Parser.js";

interface TranslationUnitLike {
getUnitHandle(): number;
}

class StackEntry {
#parser: Parser;
#parser: TranslationUnitLike;
children?: Generator<AST | Token>;

constructor(readonly owner: AST | Token, parser: Parser) {
constructor(readonly owner: AST | Token, parser: TranslationUnitLike) {
this.#parser = parser;
}

Expand Down Expand Up @@ -70,10 +74,10 @@ class StackEntry {
}

export class ASTCursor {
readonly #parser: Parser;
readonly #parser: TranslationUnitLike;
readonly #stack: StackEntry[] = [];

constructor(readonly root: AST, parser: Parser) {
constructor(readonly root: AST, parser: TranslationUnitLike) {
this.#parser = parser;
this.#stack.push(new StackEntry(root, this.#parser));
}
Expand Down
22 changes: 15 additions & 7 deletions packages/cxx-frontend/src/Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,27 @@

import { cxx } from "./cxx.js";
import { SourceLocation } from "./SourceLocation.js";
import { Parser } from "./Parser.js";
import { TokenKind } from "./TokenKind.js";

interface TranslationUnitLike {
getUnitHandle(): number;
}

export class Token {
constructor(private readonly handle: number, private readonly parser: Parser) {
#handle: number;
#unit: number;

constructor(handle: number, parser: TranslationUnitLike) {
this.#handle = handle;
this.#unit = parser.getUnitHandle();
}

getHandle() {
return this.handle;
return this.#handle;
}

getKind(): TokenKind {
return cxx.getTokenKind(this.handle, this.parser.getUnitHandle());
return cxx.getTokenKind(this.#handle, this.#unit);
}

is(kind: TokenKind) {
Expand All @@ -44,14 +52,14 @@ export class Token {
}

getText(): string {
return cxx.getTokenText(this.handle, this.parser.getUnitHandle());
return cxx.getTokenText(this.#handle, this.#unit);
}

getLocation(): SourceLocation {
return cxx.getTokenLocation(this.handle, this.parser.getUnitHandle());
return cxx.getTokenLocation(this.#handle, this.#unit);
}

static from(handle: number, parser: Parser): Token | undefined {
static from(handle: number, parser: TranslationUnitLike): Token | undefined {
return handle ? new Token(handle, parser) : undefined;
}
}
122 changes: 122 additions & 0 deletions packages/cxx-frontend/src/TranslationUnit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) 2023 Roberto Raggi <roberto.raggi@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import { AST } from "./AST.js";
import { cxx } from "./cxx.js";
import { Token } from "./Token.js";

export class TranslationUnit {
#control: typeof cxx.Control;
#diagnosticsClient: typeof cxx.DiagnosticsClient;
#handle: typeof cxx.TranslationUnit

/**
* Creates a new translation unit.
*/
constructor() {
this.#control = new cxx.Control();
this.#diagnosticsClient = new cxx.DiagnosticsClient();
this.#handle = new cxx.TranslationUnit(this.#control, this.#diagnosticsClient);
}

/**
* Disposes the translation unit.
*/
dispose() {
this.#handle.delete();
this.#diagnosticsClient.delete();
this.#control.delete();
}

/**
* Preprocesses the given source code.
*
* @param source the source code
* @param path the path of the source code
*/
preprocess(source: string, path: string) {
this.#handle.setSource(source, path);
}

/**
* Parses the preprocessed code
*
* @returns the AST or undefined
*/
parse(): AST | undefined {
if (!this.#handle.parse(false)) {
return undefined;
}

return this.getAST();
}

/**
* Returns the AST.
*
* @returns the AST or undefined
*/
getAST(): AST | undefined {
return AST.from(this.#handle.getAST(), this.#handle);
}

/**
* Returns the preprocessed tokens.
*/
tokens(): Iterable<Token> {
return {
[Symbol.iterator]: () => {
const count = this.tokenCount();
let index = 1;
return {
next: () => {
if (index < count) {
const token = this.tokenAt(index++);

if (token !== undefined) {
return { value: token, done: false };
}
}
return { value: undefined, done: true };
}
}
}
}
}

/**
* Returns the number of tokens.
*
* @returns the number of tokens
*/
tokenCount(): number {
return this.#handle.tokenCount();
}

/**
* Returns the token at the given index.
*
* @param index the index
* @returns the token or undefined
*/
tokenAt(index: number): Token | undefined {
return Token.from(index, this.#handle);
}
}
15 changes: 14 additions & 1 deletion packages/cxx-frontend/src/cxx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,20 @@ interface DiagnosticsClient {
delete(): void;
}

interface TranslationUnit {
new(control: Control, diagnosticsClient: DiagnosticsClient): TranslationUnit;
delete(): void;

getUnitHandle(): number;
setSource(source: string, path: string): void;
parse(checkTypes: boolean): boolean;
tokenCount(): number;
tokenAt(index: number): number;
getAST(): number;
}

interface Preprocessor {
new(control: Control, diagnosticClient: DiagnosticsClient): Preprocessor;
new(control: Control, diagnosticsClient: DiagnosticsClient): Preprocessor;
delete(): void;

canResolveFiles(): boolean;
Expand Down Expand Up @@ -80,6 +92,7 @@ export interface CXX {
DiagnosticsClient: DiagnosticsClient;
Preprocessor: Preprocessor;
Lexer: Lexer;
TranslationUnit: TranslationUnit;

createUnit(source: string, path: string): Unit;
getASTKind(handle: number): number;
Expand Down
1 change: 1 addition & 0 deletions packages/cxx-frontend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export * from "./TokenKind.js";
export * from "./Token.js";
export * from "./Lexer.js";
export * from "./Preprocessor.js";
export * from "./TranslationUnit.js";
11 changes: 7 additions & 4 deletions packages/cxx-gen-ast/src/gen_ast_ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function gen_ast_ts({ ast, output }: { ast: AST; output: string }) {
});

emit(
`const AST_CONSTRUCTORS: Array<new (handle: number, kind: ASTKind, parser: Parser) => AST> = [`
`const AST_CONSTRUCTORS: Array<new (handle: number, kind: ASTKind, parser: TranslationUnitLike) => AST> = [`
);
by_bases.forEach((nodes) => {
nodes.forEach(({ name }) => {
Expand All @@ -115,13 +115,16 @@ import { SourceLocation } from "./SourceLocation.js";
import { ASTCursor } from "./ASTCursor.js";
import { ASTVisitor } from "./ASTVisitor.js";
import { ASTKind } from "./ASTKind.js";
import { Parser } from "./Parser.js";
import { Token } from "./Token.js";
interface TranslationUnitLike {
getUnitHandle(): number;
}
export abstract class AST {
constructor(private readonly handle: number,
private readonly kind: ASTKind,
protected readonly parser: Parser) {
protected readonly parser: TranslationUnitLike) {
}
walk(): ASTCursor {
Expand Down Expand Up @@ -154,7 +157,7 @@ export abstract class AST {
abstract accept<Context, Result>(visitor: ASTVisitor<Context, Result>, context: Context): Result;
static from<T extends AST = AST>(handle: number, parser: Parser): T | undefined {
static from<T extends AST = AST>(handle: number, parser: TranslationUnitLike): T | undefined {
if (handle) {
const kind = cxx.getASTKind(handle) as ASTKind;
const ast = new AST_CONSTRUCTORS[kind](handle, kind, parser) as T;
Expand Down
Loading

0 comments on commit 7792714

Please sign in to comment.