From 51aa8f736aa0be0a5887451780870681cafd2ab3 Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Thu, 25 Dec 2025 19:01:30 -0800 Subject: [PATCH 1/8] initial custom ir creation --- bin/cli.js | 4 +- lib/ast/ast-builder.js | 109 +++++++++++++++++++++++++++++++++++++++ lib/ast/ir-nodes.js | 112 +++++++++++++++++++++++++++++++++++++++++ lib/compiler.js | 9 ++-- package.json | 1 - plan.md | 73 +++++++++++++++++++++++++++ 6 files changed, 300 insertions(+), 8 deletions(-) create mode 100644 lib/ast/ast-builder.js create mode 100644 lib/ast/ir-nodes.js create mode 100644 plan.md diff --git a/bin/cli.js b/bin/cli.js index 1b9a39e..f989b73 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -14,9 +14,9 @@ compile(inputStream) .then((results) => { const end = process.hrtime.bigint(); const totalTime = pretty(end - start); - const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount} AST Nodes: ${results.astNodeCount} `; + const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount}`; console.log(`// ${message}`); - console.log(`\n${results.code}`); + console.log(`\n${JSON.stringify(results.ir, null, 2)}`); }) .catch((err) => { console.error(err); diff --git a/lib/ast/ast-builder.js b/lib/ast/ast-builder.js new file mode 100644 index 0000000..e15f95e --- /dev/null +++ b/lib/ast/ast-builder.js @@ -0,0 +1,109 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ + +import { TokenType } from '../tokenizer/token-type.js'; +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from './ir-nodes.js'; + +// ASTBuilder builds an IR AST from tokens using a state machine +export class ASTBuilder { + constructor() { + this.tokens = null; + this.index = 0; + } + + // Main entry point: build AST from async generator of tokens + async build(tokens) { + const allTokens = []; + for await (const token of tokens) { + allTokens.push(token); + } + this.tokens = allTokens; + this.index = 0; + this.parse(); + return this.program; + } + + parse() { + this.program = new Program(this.parseBlock(null), null); + } + + parseBlock(endKeyword) { + const statements = []; + while (this.index < this.tokens.length) { + if (endKeyword && this.tokens[this.index].type === TokenType.keyword && this.tokens[this.index].value === endKeyword) { + this.index++; + return statements; + } + statements.push(this.parseStatement()); + } + if (endKeyword) throw new Error(`Expected '${endKeyword}'`); + return statements; + } + + parseStatement() { + const token = this.tokens[this.index++]; + if (token.type === TokenType.keyword) { + if (token.value === 'make') { + const idToken = this.tokens[this.index++]; + if (idToken.type !== TokenType.identifier) throw new Error(`Expected identifier after 'make'`); + const expr = this.parseFullExpression(); + return new VariableDeclaration(idToken.value, expr, { line: token.line, column: token.column }); + } else if (token.value === 'assign') { + const idToken = this.tokens[this.index++]; + if (idToken.type !== TokenType.identifier) throw new Error(`Expected identifier after 'assign'`); + const expr = this.parseFullExpression(); + return new AssignmentExpression(idToken.value, expr, { line: token.line, column: token.column }); + } else if (token.value === 'print') { + const expr = this.parseFullExpression(); + return new PrintStatement(expr, { line: token.line, column: token.column }); + } else if (token.value === 'if') { + const test = this.parseFullExpression(); + const consequent = this.parseBlock('endif'); + return new IfStatement(test, consequent, { line: token.line, column: token.column }); + } else if (token.value === 'as') { + const test = this.parseFullExpression(); + const body = this.parseBlock('repeat'); + return new WhileStatement(test, body, { line: token.line, column: token.column }); + } else { + throw new Error(`Unexpected keyword: ${token.value}`); + } + } else if (token.type === TokenType.identifier) { + // assignment + const opToken = this.tokens[this.index++]; + if (opToken.type !== TokenType.operator || opToken.value !== '=') throw new Error(`Expected '=' after identifier`); + const expr = this.parseFullExpression(); + return new AssignmentExpression(token.value, expr, { line: token.line, column: token.column }); + } else { + throw new Error(`Unexpected token: ${token.type} ${token.value}`); + } + } + + // Parse a full expression, including binary operations + parseFullExpression() { + const token = this.tokens[this.index++]; + let expr = this.parseExpression(token); + while (this.index < this.tokens.length && this.tokens[this.index].type === TokenType.operator) { + const opToken = this.tokens[this.index++]; + const rightToken = this.tokens[this.index++]; + const right = this.parseExpression(rightToken); + expr = new BinaryExpression(expr, opToken.value, right, expr.loc); + } + return expr; + } + + // Parse a single expression from a token + parseExpression(token) { + if (token.type === TokenType.identifier) { + return new Identifier(token.value, { line: token.line, column: token.column }); + } else if (token.type === TokenType.number) { + return new NumericLiteral(Number(token.value), { line: token.line, column: token.column }); + } else if (token.type === TokenType.string) { + return new StringLiteral(token.value.slice(1, -1), { line: token.line, column: token.column }); // Remove quotes + } else { + throw new Error(`Unexpected expression token: ${token.type} ${token.value}`); + } + } +} \ No newline at end of file diff --git a/lib/ast/ir-nodes.js b/lib/ast/ir-nodes.js new file mode 100644 index 0000000..da9ca85 --- /dev/null +++ b/lib/ast/ir-nodes.js @@ -0,0 +1,112 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ + +// Base class for all AST nodes, includes location info for error reporting +export class Node { + constructor(loc) { + this.loc = loc; // { line, column } for simplicity + } +} + +// Base class for statements +export class Statement extends Node { + constructor(loc) { + super(loc); + } +} + +// Base class for expressions +export class Expression extends Node { + constructor(loc) { + super(loc); + } +} + +// Top-level program node +export class Program extends Node { + constructor(statements, loc) { + super(loc); + this.statements = statements; // Array of Statement + } +} + +// Variable declaration (e.g., make x 5) +export class VariableDeclaration extends Statement { + constructor(identifier, value, loc) { + super(loc); + this.identifier = identifier; // string (variable name) + this.value = value; // Expression (initial value) + } +} + +// Assignment expression (e.g., x = 5 or assign x 5) +export class AssignmentExpression extends Expression { + constructor(left, right, loc) { + super(loc); + this.left = left; // string (variable name) or Expression + this.right = right; // Expression + } +} + +// Binary expression (e.g., a + b) +export class BinaryExpression extends Expression { + constructor(left, operator, right, loc) { + super(loc); + this.left = left; // Expression + this.operator = operator; // string (e.g., '+', '==') + this.right = right; // Expression + } +} + +// If statement (e.g., if condition ... endif) +export class IfStatement extends Statement { + constructor(test, consequent, loc) { + super(loc); + this.test = test; // Expression (condition) + this.consequent = consequent; // Array of Statement (body) + } +} + +// While statement (e.g., as condition ... repeat) +export class WhileStatement extends Statement { + constructor(test, body, loc) { + super(loc); + this.test = test; // Expression (condition) + this.body = body; // Array of Statement + } +} + +// Print statement (e.g., print x) +export class PrintStatement extends Statement { + constructor(argument, loc) { + super(loc); + this.argument = argument; // Expression (what to print) + } +} + +// Identifier (e.g., variable name) +export class Identifier extends Expression { + constructor(name, loc) { + super(loc); + this.name = name; // string + } +} + +// Numeric literal (e.g., 42) +export class NumericLiteral extends Expression { + constructor(value, loc) { + super(loc); + this.value = value; // number + } +} + +// String literal (e.g., 'hello') +export class StringLiteral extends Expression { + constructor(value, loc) { + super(loc); + this.value = value; // string (without quotes) + } +} \ No newline at end of file diff --git a/lib/compiler.js b/lib/compiler.js index 762364d..ca3ba46 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -5,22 +5,21 @@ */ import { Preprocessor } from './preprocessing/preprocessor.js'; import { Tokenizer } from './tokenizer/tokenizer.js'; -import { BabelAST } from './babel-ast/babel-ast.js'; +import { ASTBuilder } from './ast/ast-builder.js'; export async function compile(inputStream) { const preprocessor = new Preprocessor(); const tokenizer = new Tokenizer(); - const babelAst = new BabelAST(); + const astBuilder = new ASTBuilder(); const preprocessorGen = preprocessor.process(inputStream); const tokenizerGen = tokenizer.process(preprocessorGen); - const astResult = await babelAst.process(tokenizerGen); + const ir = await astBuilder.build(tokenizerGen); return { - code: astResult.code, + ir, preprocessorTokenCount: preprocessor.tokenCount, tokenCount: tokenizer.tokenCount, - astNodeCount: astResult.astNodeCount, }; } diff --git a/package.json b/package.json index cfa14c4..ef6cc36 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ }, "keywords": [ "compiler", - "stream", "lexer", "parser", "ast" diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..837a70f --- /dev/null +++ b/plan.md @@ -0,0 +1,73 @@ + +## Final Plan for Next Phase: Building a Custom Intermediate AST (IR) for Multi-Backend Support + +With the README done, let's outline the implementation plan for generalizing the AST to support pluggable backends (Babel JS, LLVM, Binaryen WASM, custom interpreter). We'll follow **Option 1** from our earlier analysis: Build a custom IR first, then translate to target ASTs. This keeps parsing logic shared and enables easy backend additions. + +### Phase Goals +- Decouple AST construction from Babel specifics. +- Enable backends like LLVM (inspired by llvm.js) and WASM. +- Maintain line/column propagation for error reporting. +- Keep the codebase educational and modular. + +### Step-by-Step Implementation Plan + +#### 1. **Define the Intermediate Representation (IR) Classes** (1-2 days) + - Create `lib/ast/ir-nodes.js`: Define abstract AST node classes for Complect's language features. + - Base classes: `Node` (with `loc` for line/column), `Program`, `Statement`, `Expression`. + - Specific nodes: `VariableDeclaration`, `AssignmentExpression`, `BinaryExpression`, `IfStatement`, `WhileStatement`, `PrintStatement`, etc. + - Example: + ```javascript + export class VariableDeclaration extends Statement { + constructor(identifier, value, loc) { + super(loc); + this.identifier = identifier; // string or Identifier node + this.value = value; // Expression + } + } + ``` + - Ensure IR is backend-agnostic (e.g., no JS-specific assumptions). + +#### 2. **Refactor BabelAST into IR Builder + Babel Translator** (2-3 days) + - Extract parsing logic from babel-ast.js into `lib/ast/ast-builder.js`: A new class that builds IR from tokens using the same state machine. + - Create `lib/backends/babel-translator.js`: Translates IR to Babel AST (e.g., `IR.VariableDeclaration` → `t.variableDeclaration`). + - Update compiler.js to use `ASTBuilder` for IR, then `BabelTranslator` for Babel AST. + - Test: Ensure output matches current Babel backend. + +#### 3. **Add LLVM Backend** (3-4 days) + - Create `lib/backends/llvm-translator.js`: Translates IR to LLVM IR strings (e.g., using llvm.js as a template). + - Handle LLVM specifics: Modules, functions, variables, control flow. + - Update compiler.js for backend selection (e.g., via config). + - Test: Compile simple Complect code to LLVM IR and verify with `lli` or similar. + +#### 4. **Add Binaryen (WASM) Backend** (2-3 days) + - Create `lib/backends/binaryen-translator.js`: Translates IR to Binaryen AST for WASM output. + - Integrate Binaryen library for WASM generation. + - Test: Output WASM modules from Complect code. + +#### 5. **Add Custom Interpreter Backend** (2-3 days) + - Create `lib/backends/interpreter.js`: Evaluates IR directly (e.g., `VariableDeclaration` sets a runtime variable). + - Implement a simple runtime environment (scope, execution). + - Test: Run Complect code without external compilation. + +#### 6. **Backend Configuration and CLI Updates** (1 day) + - Add backend selection to compiler.js (e.g., `--backend babel|llvm|wasm|interpreter`). + - Update cli.js to pass backend option. + - Update tests for multiple backends. + +#### 7. **Testing, Documentation, and Polish** (2-3 days) + - Update all tests to cover IR building and translations. + - Add integration tests for backends. + - Update README.md with backend usage examples. + - Ensure line/column errors propagate through IR. + +### Timeline and Milestones +- **Total Time**: ~2-3 weeks (part-time). +- **Milestone 1**: IR classes + Babel refactor (Week 1). +- **Milestone 2**: LLVM backend (Week 2). +- **Milestone 3**: WASM + Interpreter backends, config, tests (Week 3). +- **Risks**: LLVM/Binaryen integration may require learning curves; start with simple backends. +- **Dependencies**: Install `binaryen` npm package for WASM. + +This plan builds on your current solid foundation, making Complect a true multi-target compiler. Ready to start with Step 1? I can provide code snippets for the IR classes! 🚀 + +Made changes. \ No newline at end of file From 0de89964a076f44906aecc393e77fe537c26e650 Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Thu, 25 Dec 2025 19:06:34 -0800 Subject: [PATCH 2/8] translator for custom ast to babel ast --- bin/cli.js | 4 +- lib/ast/ast-builder.js | 20 +++--- lib/ast/ir-nodes.js | 6 +- lib/babel-ast/babel-translator.js | 105 ++++++++++++++++++++++++++++++ lib/compiler.js | 5 ++ 5 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 lib/babel-ast/babel-translator.js diff --git a/bin/cli.js b/bin/cli.js index f989b73..1b9a39e 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -14,9 +14,9 @@ compile(inputStream) .then((results) => { const end = process.hrtime.bigint(); const totalTime = pretty(end - start); - const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount}`; + const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount} AST Nodes: ${results.astNodeCount} `; console.log(`// ${message}`); - console.log(`\n${JSON.stringify(results.ir, null, 2)}`); + console.log(`\n${results.code}`); }) .catch((err) => { console.error(err); diff --git a/lib/ast/ast-builder.js b/lib/ast/ast-builder.js index e15f95e..393b299 100644 --- a/lib/ast/ast-builder.js +++ b/lib/ast/ast-builder.js @@ -50,23 +50,23 @@ export class ASTBuilder { const idToken = this.tokens[this.index++]; if (idToken.type !== TokenType.identifier) throw new Error(`Expected identifier after 'make'`); const expr = this.parseFullExpression(); - return new VariableDeclaration(idToken.value, expr, { line: token.line, column: token.column }); + return new VariableDeclaration(idToken.value, expr, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.value === 'assign') { const idToken = this.tokens[this.index++]; if (idToken.type !== TokenType.identifier) throw new Error(`Expected identifier after 'assign'`); const expr = this.parseFullExpression(); - return new AssignmentExpression(idToken.value, expr, { line: token.line, column: token.column }); + return new AssignmentExpression(idToken.value, expr, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.value === 'print') { const expr = this.parseFullExpression(); - return new PrintStatement(expr, { line: token.line, column: token.column }); + return new PrintStatement(expr, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.value === 'if') { const test = this.parseFullExpression(); const consequent = this.parseBlock('endif'); - return new IfStatement(test, consequent, { line: token.line, column: token.column }); + return new IfStatement(test, consequent, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.value === 'as') { const test = this.parseFullExpression(); const body = this.parseBlock('repeat'); - return new WhileStatement(test, body, { line: token.line, column: token.column }); + return new WhileStatement(test, body, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else { throw new Error(`Unexpected keyword: ${token.value}`); } @@ -75,7 +75,7 @@ export class ASTBuilder { const opToken = this.tokens[this.index++]; if (opToken.type !== TokenType.operator || opToken.value !== '=') throw new Error(`Expected '=' after identifier`); const expr = this.parseFullExpression(); - return new AssignmentExpression(token.value, expr, { line: token.line, column: token.column }); + return new AssignmentExpression(token.value, expr, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else { throw new Error(`Unexpected token: ${token.type} ${token.value}`); } @@ -89,7 +89,7 @@ export class ASTBuilder { const opToken = this.tokens[this.index++]; const rightToken = this.tokens[this.index++]; const right = this.parseExpression(rightToken); - expr = new BinaryExpression(expr, opToken.value, right, expr.loc); + expr = new BinaryExpression(expr, opToken.value, right, { start: expr.loc.start, end: right.loc.end }); } return expr; } @@ -97,11 +97,11 @@ export class ASTBuilder { // Parse a single expression from a token parseExpression(token) { if (token.type === TokenType.identifier) { - return new Identifier(token.value, { line: token.line, column: token.column }); + return new Identifier(token.value, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.type === TokenType.number) { - return new NumericLiteral(Number(token.value), { line: token.line, column: token.column }); + return new NumericLiteral(Number(token.value), { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.type === TokenType.string) { - return new StringLiteral(token.value.slice(1, -1), { line: token.line, column: token.column }); // Remove quotes + return new StringLiteral(token.value, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); // Already without quotes from preprocessor } else { throw new Error(`Unexpected expression token: ${token.type} ${token.value}`); } diff --git a/lib/ast/ir-nodes.js b/lib/ast/ir-nodes.js index da9ca85..2357e58 100644 --- a/lib/ast/ir-nodes.js +++ b/lib/ast/ir-nodes.js @@ -7,7 +7,7 @@ // Base class for all AST nodes, includes location info for error reporting export class Node { constructor(loc) { - this.loc = loc; // { line, column } for simplicity + this.loc = loc; // { start: { line, column }, end: { line, column } } } } @@ -43,10 +43,10 @@ export class VariableDeclaration extends Statement { } // Assignment expression (e.g., x = 5 or assign x 5) -export class AssignmentExpression extends Expression { +export class AssignmentExpression extends Statement { constructor(left, right, loc) { super(loc); - this.left = left; // string (variable name) or Expression + this.left = left; // string (variable name) this.right = right; // Expression } } diff --git a/lib/babel-ast/babel-translator.js b/lib/babel-ast/babel-translator.js new file mode 100644 index 0000000..cf0c7c8 --- /dev/null +++ b/lib/babel-ast/babel-translator.js @@ -0,0 +1,105 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ + +import * as t from '@babel/types'; +import { CodeGenerator } from '@babel/generator'; +import _traverse from "@babel/traverse"; +const traverse = _traverse.default; + +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; + +export class BabelTranslator { + translate(ir) { + const program = this.translateProgram(ir); + const file = t.file(program); + const cg = new CodeGenerator(file); + + let nodeCount = 0; + traverse(cg._ast, { + enter() { + nodeCount++; + } + }); + + const output = cg.generate({ sourceMaps: true }); + return { + code: output.code, + astNodeCount: nodeCount + }; + } + + translateProgram(program) { + const statements = program.statements.map(s => this.translateStatement(s)); + return t.program(statements, [], 'script'); + } + + translateStatement(stmt) { + if (stmt instanceof VariableDeclaration) { + const id = t.identifier(stmt.identifier); + id.loc = stmt.loc; + const init = this.translateExpression(stmt.value); + const declarator = t.variableDeclarator(id, init); + const decl = t.variableDeclaration('let', [declarator]); + decl.loc = stmt.loc; + return decl; + } else if (stmt instanceof AssignmentExpression) { + const left = t.identifier(stmt.left); + left.loc = stmt.loc; + const right = this.translateExpression(stmt.right); + const assign = t.assignmentExpression('=', left, right); + assign.loc = stmt.loc; + return t.expressionStatement(assign); + } else if (stmt instanceof IfStatement) { + const test = this.translateExpression(stmt.test); + const consequent = t.blockStatement(stmt.consequent.map(s => this.translateStatement(s))); + const ifStmt = t.ifStatement(test, consequent); + ifStmt.loc = stmt.loc; + return ifStmt; + } else if (stmt instanceof WhileStatement) { + const test = this.translateExpression(stmt.test); + const body = t.blockStatement(stmt.body.map(s => this.translateStatement(s))); + const whileStmt = t.whileStatement(test, body); + whileStmt.loc = stmt.loc; + return whileStmt; + } else if (stmt instanceof PrintStatement) { + const arg = this.translateExpression(stmt.argument); + const consoleId = t.identifier('console'); + consoleId.loc = stmt.loc; + const logId = t.identifier('log'); + const member = t.memberExpression(consoleId, logId); + const call = t.callExpression(member, [arg]); + const exprStmt = t.expressionStatement(call); + exprStmt.loc = stmt.loc; + return exprStmt; + } else { + throw new Error(`Unknown statement type: ${stmt.constructor.name}`); + } + } + + translateExpression(expr) { + if (expr instanceof Identifier) { + const id = t.identifier(expr.name); + id.loc = expr.loc; + return id; + } else if (expr instanceof NumericLiteral) { + const num = t.numericLiteral(expr.value); + num.loc = expr.loc; + return num; + } else if (expr instanceof StringLiteral) { + const str = t.stringLiteral(expr.value); + str.loc = expr.loc; + return str; + } else if (expr instanceof BinaryExpression) { + const left = this.translateExpression(expr.left); + const right = this.translateExpression(expr.right); + const bin = t.binaryExpression(expr.operator, left, right); + bin.loc = expr.loc; + return bin; + } else { + throw new Error(`Unknown expression type: ${expr.constructor.name}`); + } + } +} \ No newline at end of file diff --git a/lib/compiler.js b/lib/compiler.js index ca3ba46..ebcf670 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -6,20 +6,25 @@ import { Preprocessor } from './preprocessing/preprocessor.js'; import { Tokenizer } from './tokenizer/tokenizer.js'; import { ASTBuilder } from './ast/ast-builder.js'; +import { BabelTranslator } from './babel-ast/babel-translator.js'; export async function compile(inputStream) { const preprocessor = new Preprocessor(); const tokenizer = new Tokenizer(); const astBuilder = new ASTBuilder(); + const babelTranslator = new BabelTranslator(); const preprocessorGen = preprocessor.process(inputStream); const tokenizerGen = tokenizer.process(preprocessorGen); const ir = await astBuilder.build(tokenizerGen); + const result = babelTranslator.translate(ir); return { + code: result.code, ir, preprocessorTokenCount: preprocessor.tokenCount, tokenCount: tokenizer.tokenCount, + astNodeCount: result.astNodeCount, }; } From 12e7131a505602cb77184c91e27ca3633dd3de0e Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Thu, 25 Dec 2025 19:16:27 -0800 Subject: [PATCH 3/8] add new tests --- lib/ast/ast-builder.test.js | 163 +++++++++++++++++++++++++ lib/babel-ast/babel-translator.test.js | 157 ++++++++++++++++++++++++ lib/tokenizer/token.js | 4 +- 3 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 lib/ast/ast-builder.test.js create mode 100644 lib/babel-ast/babel-translator.test.js diff --git a/lib/ast/ast-builder.test.js b/lib/ast/ast-builder.test.js new file mode 100644 index 0000000..df5a8f0 --- /dev/null +++ b/lib/ast/ast-builder.test.js @@ -0,0 +1,163 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ +import { describe, it } from "node:test"; +import assert from "node:assert"; + +import { ASTBuilder } from "./ast-builder.js"; +import { TokenType } from "../tokenizer/token-type.js"; +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from "./ir-nodes.js"; + +async function* tokenGenerator(tokens) { + for (const token of tokens) { + yield token; + } +} + +describe('ASTBuilder', () => { + it('variable declaration with number', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'make', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'x', line: 1, column: 6 }, + { type: TokenType.number, value: '5', line: 1, column: 8 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir instanceof Program); + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof VariableDeclaration); + assert(stmt.identifier === 'x'); + assert(stmt.value instanceof NumericLiteral); + assert(stmt.value.value === 5); + }); + + it('variable declaration with string', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'make', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'msg', line: 1, column: 6 }, + { type: TokenType.string, value: 'hello', line: 1, column: 10 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof VariableDeclaration); + assert(stmt.identifier === 'msg'); + assert(stmt.value instanceof StringLiteral); + assert(stmt.value.value === 'hello'); + }); + + it('assign statement', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'assign', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'x', line: 1, column: 8 }, + { type: TokenType.number, value: '10', line: 1, column: 10 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof AssignmentExpression); + assert(stmt.left === 'x'); + assert(stmt.right instanceof NumericLiteral); + assert(stmt.right.value === 10); + }); + + it('assignment expression with binary', async () => { + const tokens = [ + { type: TokenType.identifier, value: 'x', line: 1, column: 1 }, + { type: TokenType.operator, value: '=', line: 1, column: 3 }, + { type: TokenType.identifier, value: 'a', line: 1, column: 5 }, + { type: TokenType.operator, value: '+', line: 1, column: 7 }, + { type: TokenType.identifier, value: 'b', line: 1, column: 9 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof AssignmentExpression); + assert(stmt.left === 'x'); + assert(stmt.right instanceof BinaryExpression); + assert(stmt.right.operator === '+'); + assert(stmt.right.left instanceof Identifier); + assert(stmt.right.left.name === 'a'); + assert(stmt.right.right instanceof Identifier); + assert(stmt.right.right.name === 'b'); + }); + + it('print statement', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'print', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'x', line: 1, column: 7 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof PrintStatement); + assert(stmt.argument instanceof Identifier); + assert(stmt.argument.name === 'x'); + }); + + it('if statement', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'if', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'x', line: 1, column: 4 }, + { type: TokenType.operator, value: '>', line: 1, column: 6 }, + { type: TokenType.number, value: '0', line: 1, column: 8 }, + { type: TokenType.keyword, value: 'print', line: 2, column: 3 }, + { type: TokenType.identifier, value: 'x', line: 2, column: 9 }, + { type: TokenType.keyword, value: 'endif', line: 3, column: 1 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof IfStatement); + assert(stmt.test instanceof BinaryExpression); + assert(stmt.test.operator === '>'); + assert(stmt.consequent.length === 1); + assert(stmt.consequent[0] instanceof PrintStatement); + }); + + it('while statement', async () => { + const tokens = [ + { type: TokenType.keyword, value: 'as', line: 1, column: 1 }, + { type: TokenType.identifier, value: 'x', line: 1, column: 4 }, + { type: TokenType.operator, value: '>', line: 1, column: 6 }, + { type: TokenType.number, value: '0', line: 1, column: 8 }, + { type: TokenType.identifier, value: 'x', line: 2, column: 3 }, + { type: TokenType.operator, value: '=', line: 2, column: 5 }, + { type: TokenType.identifier, value: 'x', line: 2, column: 7 }, + { type: TokenType.operator, value: '-', line: 2, column: 9 }, + { type: TokenType.number, value: '1', line: 2, column: 11 }, + { type: TokenType.keyword, value: 'repeat', line: 3, column: 1 } + ]; + + const builder = new ASTBuilder(); + const ir = await builder.build(tokenGenerator(tokens)); + + assert(ir.statements.length === 1); + const stmt = ir.statements[0]; + assert(stmt instanceof WhileStatement); + assert(stmt.test instanceof BinaryExpression); + assert(stmt.test.operator === '>'); + assert(stmt.body.length === 1); + assert(stmt.body[0] instanceof AssignmentExpression); + }); +}); \ No newline at end of file diff --git a/lib/babel-ast/babel-translator.test.js b/lib/babel-ast/babel-translator.test.js new file mode 100644 index 0000000..69c7fd0 --- /dev/null +++ b/lib/babel-ast/babel-translator.test.js @@ -0,0 +1,157 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ +import { describe, it } from "node:test"; +import assert from "node:assert"; + +import { BabelTranslator } from "./babel-translator.js"; +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from "../ast/ir-nodes.js"; + +describe('BabelTranslator', () => { + it('translates variable declaration with number', () => { + const ir = new Program([ + new VariableDeclaration('x', new NumericLiteral(5, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'let x = 5;'); + }); + + it('translates variable declaration with string', () => { + const ir = new Program([ + new VariableDeclaration('msg', new StringLiteral('hello', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'let msg = "hello";'); + }); + + it('translates assignment expression', () => { + const ir = new Program([ + new AssignmentExpression('x', new NumericLiteral(10, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'x = 10;'); + }); + + it('translates binary expression', () => { + const ir = new Program([ + new AssignmentExpression('x', new BinaryExpression( + new Identifier('a', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '+', + new Identifier('b', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'x = a + b;'); + }); + + it('translates print statement', () => { + const ir = new Program([ + new PrintStatement(new Identifier('x', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'console.log(x);'); + }); + + it('translates if statement', () => { + const ir = new Program([ + new IfStatement( + new BinaryExpression( + new Identifier('x', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '>', + new NumericLiteral(0, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), + [ + new PrintStatement(new Identifier('x', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'if (x > 0) {\n console.log(x);\n}'); + }); + + it('translates while statement', () => { + const ir = new Program([ + new WhileStatement( + new BinaryExpression( + new Identifier('x', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '>', + new NumericLiteral(0, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), + [ + new AssignmentExpression('x', new BinaryExpression( + new Identifier('x', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '-', + new NumericLiteral(1, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + assert(result.code.trim() === 'while (x > 0) {\n x = x - 1;\n}'); + }); + + it('translates complex program', () => { + const ir = new Program([ + new VariableDeclaration('a', new NumericLiteral(0, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + new VariableDeclaration('b', new NumericLiteral(1, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + new WhileStatement( + new BinaryExpression( + new Identifier('a', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '<', + new NumericLiteral(10, { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), + [ + new PrintStatement(new Identifier('a', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + new AssignmentExpression('a', new BinaryExpression( + new Identifier('a', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + '+', + new Identifier('b', { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }), + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ), { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } }) + ], + { start: { line: 1, column: 1 }, end: { line: 1, column: 1 } } + ) + ], null); + + const translator = new BabelTranslator(); + const result = translator.translate(ir); + + const expected = `let a = 0; +let b = 1; +while (a < 10) { + console.log(a); + a = a + b; +}`; + assert(result.code.trim() === expected); + }); +}); \ No newline at end of file diff --git a/lib/tokenizer/token.js b/lib/tokenizer/token.js index a7b201a..e1a0afd 100644 --- a/lib/tokenizer/token.js +++ b/lib/tokenizer/token.js @@ -12,9 +12,7 @@ export class Token { this.line = line; this.column = column; } - // append(ch) { - // this.value += ch; - // } + toString() { return `${this.type} ${this.value}`; } From dc831e7d2acbb88c772df67cbec030f41689cb80 Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Fri, 26 Dec 2025 01:01:41 -0800 Subject: [PATCH 4/8] llvm translator from our ast into llvm ir --- .gitignore | 2 + bin/cli.js | 18 +- lib/compiler.js | 14 +- lib/llvm/llvm-translator.js | 394 +++++++++ llvm-test/llvm.js | 3 +- package-lock.json | 1631 ++++++++++++++++++++++++++++++++++- package.json | 3 +- 7 files changed, 2043 insertions(+), 22 deletions(-) create mode 100644 lib/llvm/llvm-translator.js diff --git a/.gitignore b/.gitignore index 76efb07..8fd3da1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .vscode +*.ll +*.s \ No newline at end of file diff --git a/bin/cli.js b/bin/cli.js index 1b9a39e..054c4f2 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -7,15 +7,29 @@ import { compile } from '../lib/compiler.js'; import { pretty } from '../lib/util/pretty-hrtime.js'; +const args = process.argv.slice(2); +let backend = 'babel'; + +if (args.length > 0 && args[0] === '--backend') { + backend = args[1]; + // Remove the backend args + process.argv.splice(2, 2); +} + const inputStream = process.stdin; const start = process.hrtime.bigint(); -compile(inputStream) +compile(inputStream, backend) .then((results) => { const end = process.hrtime.bigint(); const totalTime = pretty(end - start); const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount} AST Nodes: ${results.astNodeCount} `; - console.log(`// ${message}`); + + if (backend === 'llvm') { + console.log(`; ${message}`); + } else { + console.log(`// ${message}`); + } console.log(`\n${results.code}`); }) .catch((err) => { diff --git a/lib/compiler.js b/lib/compiler.js index ebcf670..f38400a 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -7,17 +7,25 @@ import { Preprocessor } from './preprocessing/preprocessor.js'; import { Tokenizer } from './tokenizer/tokenizer.js'; import { ASTBuilder } from './ast/ast-builder.js'; import { BabelTranslator } from './babel-ast/babel-translator.js'; +import { LLVMTranslator } from './llvm/llvm-translator.js'; -export async function compile(inputStream) { +export async function compile(inputStream, backend = 'babel') { const preprocessor = new Preprocessor(); const tokenizer = new Tokenizer(); const astBuilder = new ASTBuilder(); - const babelTranslator = new BabelTranslator(); const preprocessorGen = preprocessor.process(inputStream); const tokenizerGen = tokenizer.process(preprocessorGen); const ir = await astBuilder.build(tokenizerGen); - const result = babelTranslator.translate(ir); + + let result; + if (backend === 'llvm') { + const llvmTranslator = new LLVMTranslator(); + result = { code: llvmTranslator.translate(ir), astNodeCount: 0 }; // TODO: count LLVM nodes + } else { + const babelTranslator = new BabelTranslator(); + result = babelTranslator.translate(ir); + } return { code: result.code, diff --git a/lib/llvm/llvm-translator.js b/lib/llvm/llvm-translator.js new file mode 100644 index 0000000..92f9fd6 --- /dev/null +++ b/lib/llvm/llvm-translator.js @@ -0,0 +1,394 @@ +/* Complect - Compiler for the Complect programming language + * + * Copyright © 2024 Jarrod Connolly + * MIT License +*/ + +import llvm from 'llvm-bindings'; +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; + +export class LLVMTranslator { + constructor() { + this.context = new llvm.LLVMContext(); + this.module = null; + this.builder = null; + this.variables = new Map(); // name -> { type: 'int'|'string', value: LLVM Value } + this.function = null; + this.stringLiterals = new Map(); // value -> global string constant + } + + translate(ir) { + this.module = new llvm.Module('complect', this.context); + this.module.setTargetTriple('x86_64-pc-linux-gnu'); + this.builder = new llvm.IRBuilder(this.context); + + // Declare runtime functions + this.declareRuntimeFunctions(); + + // Create main function + const mainType = llvm.FunctionType.get(this.builder.getInt32Ty(), [], false); + this.function = llvm.Function.Create(mainType, llvm.Function.LinkageTypes.ExternalLinkage, 'main', this.module); + + const entryBB = llvm.BasicBlock.Create(this.context, 'entry', this.function); + this.builder.SetInsertPoint(entryBB); + + // Translate statements + for (const stmt of ir.statements) { + this.translateStatement(stmt); + } + + // Return 0 + this.builder.CreateRet(this.builder.getInt32(0)); + + // Verify + if (llvm.verifyFunction(this.function)) { + throw new Error('Function verification failed'); + } + if (llvm.verifyModule(this.module)) { + throw new Error('Module verification failed'); + } + + return this.module.print(); + } + + declareRuntimeFunctions() { + // Declare printf + const printfType = llvm.FunctionType.get( + this.builder.getInt32Ty(), + [this.builder.getInt8PtrTy()], + true + ); + this.module.getOrInsertFunction('printf', printfType); + + // Declare string functions + const mallocType = llvm.FunctionType.get(this.builder.getInt8PtrTy(), [this.builder.getInt64Ty()], false); + this.module.getOrInsertFunction('malloc', mallocType); + + const strcpyType = llvm.FunctionType.get(this.builder.getInt8PtrTy(), [this.builder.getInt8PtrTy(), this.builder.getInt8PtrTy()], false); + this.module.getOrInsertFunction('strcpy', strcpyType); + + const strcatType = llvm.FunctionType.get(this.builder.getInt8PtrTy(), [this.builder.getInt8PtrTy(), this.builder.getInt8PtrTy()], false); + this.module.getOrInsertFunction('strcat', strcatType); + + const strcmpType = llvm.FunctionType.get(this.builder.getInt32Ty(), [this.builder.getInt8PtrTy(), this.builder.getInt8PtrTy()], false); + this.module.getOrInsertFunction('strcmp', strcmpType); + + const sprintfType = llvm.FunctionType.get(this.builder.getInt32Ty(), [this.builder.getInt8PtrTy(), this.builder.getInt8PtrTy()], true); + this.module.getOrInsertFunction('sprintf', sprintfType); + + const strlenType = llvm.FunctionType.get(this.builder.getInt64Ty(), [this.builder.getInt8PtrTy()], false); + this.module.getOrInsertFunction('strlen', strlenType); + } + + translateStatement(stmt) { + if (stmt instanceof VariableDeclaration) { + const value = this.translateExpression(stmt.value); + const varInfo = { type: this.getExpressionType(stmt.value), value: null }; + + if (varInfo.type === 'int') { + // Create alloca for int variable + const alloca = this.builder.CreateAlloca(this.builder.getInt32Ty(), null, stmt.identifier); + this.builder.CreateStore(value, alloca); + varInfo.value = alloca; + } else if (varInfo.type === 'string') { + // Create alloca for string variable (i8**) + const alloca = this.builder.CreateAlloca(this.builder.getInt8PtrTy(), null, stmt.identifier); + this.builder.CreateStore(value, alloca); + varInfo.value = alloca; + } + + this.variables.set(stmt.identifier, varInfo); + + } else if (stmt instanceof AssignmentExpression) { + const value = this.translateExpression(stmt.right); + const varInfo = this.variables.get(stmt.left); + if (!varInfo) { + throw new Error(`Undefined variable: ${stmt.left}`); + } + this.builder.CreateStore(value, varInfo.value); + + } else if (stmt instanceof PrintStatement) { + this.translatePrint(stmt); + + } else if (stmt instanceof IfStatement) { + this.translateIf(stmt); + + } else if (stmt instanceof WhileStatement) { + this.translateWhile(stmt); + + } else { + throw new Error(`Unknown statement type: ${stmt.constructor.name}`); + } + } + + translateExpression(expr) { + if (expr instanceof Identifier) { + const varInfo = this.variables.get(expr.name); + if (!varInfo) { + throw new Error(`Undefined variable: ${expr.name}`); + } + return this.builder.CreateLoad(varInfo.type === 'int' ? this.builder.getInt32Ty() : this.builder.getInt8PtrTy(), varInfo.value, expr.name); + + } else if (expr instanceof NumericLiteral) { + return this.builder.getInt32(expr.value); + + } else if (expr instanceof StringLiteral) { + return this.getStringConstant(expr.value); + + } else if (expr instanceof BinaryExpression) { + return this.translateBinaryExpression(expr); + + } else { + throw new Error(`Unknown expression type: ${expr.constructor.name}`); + } + } + + translateBinaryExpression(expr) { + const left = this.translateExpression(expr.left); + const right = this.translateExpression(expr.right); + const leftType = this.getExpressionType(expr.left); + const rightType = this.getExpressionType(expr.right); + + switch (expr.operator) { + case '+': + if (leftType === 'string' || rightType === 'string') { + return this.stringConcat(left, right, leftType, rightType); + } else { + return this.builder.CreateAdd(left, right, 'add'); + } + case '-': return this.builder.CreateSub(left, right, 'sub'); + case '*': return this.builder.CreateMul(left, right, 'mul'); + case '/': return this.builder.CreateSDiv(left, right, 'div'); + case '%': return this.builder.CreateSRem(left, right, 'rem'); + case '==': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'eq'); + } else { + return this.builder.CreateICmpEQ(left, right, 'eq'); + } + case '!=': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'ne'); + } else { + return this.builder.CreateICmpNE(left, right, 'ne'); + } + case '<': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'lt'); + } else { + return this.builder.CreateICmpSLT(left, right, 'lt'); + } + case '<=': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'le'); + } else { + return this.builder.CreateICmpSLE(left, right, 'le'); + } + case '>': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'gt'); + } else { + return this.builder.CreateICmpSGT(left, right, 'gt'); + } + case '>=': + if (leftType === 'string' && rightType === 'string') { + return this.stringCompare(left, right, 'ge'); + } else { + return this.builder.CreateICmpSGE(left, right, 'ge'); + } + default: throw new Error(`Unknown operator: ${expr.operator}`); + } + } + + getExpressionType(expr) { + if (expr instanceof Identifier) { + const varInfo = this.variables.get(expr.name); + return varInfo ? varInfo.type : 'int'; // fallback + } else if (expr instanceof NumericLiteral) { + return 'int'; + } else if (expr instanceof StringLiteral) { + return 'string'; + } else if (expr instanceof BinaryExpression) { + // For binary expressions, we need to determine the result type + const leftType = this.getExpressionType(expr.left); + const rightType = this.getExpressionType(expr.right); + + if (expr.operator === '+') { + // String concatenation if either operand is string + if (leftType === 'string' || rightType === 'string') { + return 'string'; + } + } + + // For comparisons, result is always int (boolean) + if (['==', '!=', '<', '<=', '>', '>='].includes(expr.operator)) { + return 'int'; + } + + // Default to int for arithmetic + return 'int'; + } + return 'int'; + } + + getStringConstant(value) { + if (this.stringLiterals.has(value)) { + return this.stringLiterals.get(value); + } + + const strConst = this.builder.CreateGlobalStringPtr(value, `.str.${this.stringLiterals.size}`, 0, this.module); + this.stringLiterals.set(value, strConst); + return strConst; + } + + stringConcat(left, right, leftType, rightType) { + // Convert integers to strings if needed + let leftStr = left; + let rightStr = right; + + if (leftType === 'int') { + leftStr = this.intToString(left); + } + if (rightType === 'int') { + rightStr = this.intToString(right); + } + + // Allocate space for concatenated string + const leftLen = this.builder.CreateCall(this.module.getFunction('strlen'), [leftStr], 'leftlen'); + const rightLen = this.builder.CreateCall(this.module.getFunction('strlen'), [rightStr], 'rightlen'); + const totalLen = this.builder.CreateAdd(this.builder.CreateAdd(leftLen, rightLen, 'total'), this.builder.getInt64(1), 'totalplus1'); + const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [totalLen], 'buffer'); + + // Copy first string + this.builder.CreateCall(this.module.getFunction('strcpy'), [buffer, leftStr], 'copy1'); + // Concatenate second string + this.builder.CreateCall(this.module.getFunction('strcat'), [buffer, rightStr], 'concat'); + + return buffer; + } + + stringCompare(left, right, op) { + const strcmpFunc = this.module.getFunction('strcmp'); + const result = this.builder.CreateCall(strcmpFunc, [left, right], 'strcmp'); + + switch (op) { + case 'eq': return this.builder.CreateICmpEQ(result, this.builder.getInt32(0), 'streq'); + case 'ne': return this.builder.CreateICmpNE(result, this.builder.getInt32(0), 'strne'); + case 'lt': return this.builder.CreateICmpSLT(result, this.builder.getInt32(0), 'strlt'); + case 'le': return this.builder.CreateICmpSLE(result, this.builder.getInt32(0), 'strle'); + case 'gt': return this.builder.CreateICmpSGT(result, this.builder.getInt32(0), 'strgt'); + case 'ge': return this.builder.CreateICmpSGE(result, this.builder.getInt32(0), 'strge'); + } + } + + intToString(value) { + // Allocate buffer for number string (up to 32 digits should be enough) + const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [this.builder.getInt64(32)], 'intbuf'); + const formatStr = this.getStringConstant('%d'); + this.builder.CreateCall(this.module.getFunction('sprintf'), [buffer, formatStr, value], 'sprintf'); + return buffer; + } + + getExpressionType(expr) { + if (expr instanceof Identifier) { + const varInfo = this.variables.get(expr.name); + return varInfo ? varInfo.type : 'int'; // fallback + } else if (expr instanceof NumericLiteral) { + return 'int'; + } else if (expr instanceof StringLiteral) { + return 'string'; + } else if (expr instanceof BinaryExpression) { + // For binary expressions, we need to determine the result type + const leftType = this.getExpressionType(expr.left); + const rightType = this.getExpressionType(expr.right); + + if (expr.operator === '+') { + // String concatenation if either operand is string + if (leftType === 'string' || rightType === 'string') { + return 'string'; + } + } + + // For comparisons, result is always int (boolean) + if (['==', '!=', '<', '<=', '>', '>='].includes(expr.operator)) { + return 'int'; + } + + // Default to int for arithmetic + return 'int'; + } + return 'int'; + } + + getStringConstant(value) { + if (this.stringLiterals.has(value)) { + return this.stringLiterals.get(value); + } + + const strConst = this.builder.CreateGlobalStringPtr(value, `.str.${this.stringLiterals.size}`, 0, this.module); + this.stringLiterals.set(value, strConst); + return strConst; + } + + translatePrint(stmt) { + const printfFunc = this.module.getFunction('printf'); + const argType = this.getExpressionType(stmt.argument); + + let formatStr; + if (argType === 'string') { + formatStr = this.getStringConstant('%s\n'); + } else { + formatStr = this.getStringConstant('%d\n'); + } + + const value = this.translateExpression(stmt.argument); + this.builder.CreateCall(printfFunc, [formatStr, value], 'print'); + } + + translateIf(stmt) { + const cond = this.translateExpression(stmt.test); + + const thenBB = llvm.BasicBlock.Create(this.context, 'then', this.function); + const elseBB = llvm.BasicBlock.Create(this.context, 'else', this.function); + const mergeBB = llvm.BasicBlock.Create(this.context, 'merge', this.function); + + this.builder.CreateCondBr(cond, thenBB, elseBB); + + // Then block + this.builder.SetInsertPoint(thenBB); + for (const s of stmt.consequent) { + this.translateStatement(s); + } + this.builder.CreateBr(mergeBB); + + // Else block (empty for now) + this.builder.SetInsertPoint(elseBB); + this.builder.CreateBr(mergeBB); + + // Merge + this.builder.SetInsertPoint(mergeBB); + } + + translateWhile(stmt) { + const condBB = llvm.BasicBlock.Create(this.context, 'cond', this.function); + const bodyBB = llvm.BasicBlock.Create(this.context, 'body', this.function); + const exitBB = llvm.BasicBlock.Create(this.context, 'exit', this.function); + + this.builder.CreateBr(condBB); + + // Condition + this.builder.SetInsertPoint(condBB); + const cond = this.translateExpression(stmt.test); + this.builder.CreateCondBr(cond, bodyBB, exitBB); + + // Body + this.builder.SetInsertPoint(bodyBB); + for (const s of stmt.body) { + this.translateStatement(s); + } + this.builder.CreateBr(condBB); + + // Exit + this.builder.SetInsertPoint(exitBB); + } +} \ No newline at end of file diff --git a/llvm-test/llvm.js b/llvm-test/llvm.js index e77828c..e8f16de 100644 --- a/llvm-test/llvm.js +++ b/llvm-test/llvm.js @@ -17,7 +17,8 @@ import llvm from 'llvm-bindings'; function main() { const context = new llvm.LLVMContext(); const module = new llvm.Module('demo', context); - module.setTargetTriple('x86_64-apple-macosx12.0.0'); + //module.setTargetTriple('x86_64-apple-macosx12.0.0'); + module.setTargetTriple('x86_64-pc-linux-gnu'); const builder = new llvm.IRBuilder(context); const returnType = builder.getInt32Ty(); diff --git a/package-lock.json b/package-lock.json index afa5387..733facd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@babel/generator": "7.28.5", "@babel/parser": "7.28.5", "@babel/traverse": "^7.28.5", - "@babel/types": "7.28.5" + "@babel/types": "7.28.5", + "llvm-bindings": "0.4.2" }, "bin": { "complect": "bin/cli.js" @@ -448,6 +449,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -463,6 +479,53 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + } + }, + "node_modules/are-we-there-yet/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -470,24 +533,91 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==", + "license": "MIT" + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -498,6 +628,27 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -514,6 +665,74 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/cmake-js": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.2.tgz", + "integrity": "sha512-7MfiQ/ijzeE2kO+WFB9bv4QP5Dn2yVaAP2acFJr4NIFy2hT4w6O4EpOTLNcohR5IPX7M4wNf/5taIqMj7UA9ug==", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "bluebird": "^3", + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cmake-js/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -536,7 +755,12 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, "node_modules/cross-spawn": { @@ -570,12 +794,81 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -771,6 +1064,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -806,6 +1105,103 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "license": "ISC", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -831,6 +1227,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -840,6 +1242,12 @@ "node": ">=8" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -876,6 +1284,38 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -885,6 +1325,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -897,11 +1349,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha512-tLn1j3wYSL6DkvEI+V/j0pKohpa5jk+ER74v6S4SgCXnjS0WA+DoZbwZBrrhgwksMvtuwndyGeG5F8YMsoBzSA==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/js-tokens": { @@ -954,6 +1417,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -963,6 +1435,18 @@ "json-buffer": "3.0.1" } }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "license": "MIT", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -976,6 +1460,24 @@ "node": ">= 0.8.0" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, + "node_modules/llvm-bindings": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/llvm-bindings/-/llvm-bindings-0.4.2.tgz", + "integrity": "sha512-WFFOeG23u1AkxSdJdbrPqxJtHKTlti8rEC2Hc5yvQyrGk352+Y844DZayhevPK0j+kzLpnJfuhqssKqZBmE1Sw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "cmake-js": "^6.3.2", + "node-addon-api": "^5.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -991,17 +1493,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==", + "license": "MIT" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "license": "MIT" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", + "license": "MIT" + }, + "node_modules/memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha512-q0D3m846qY6ZkIt+19ZemU5vH56lpOZZwoJc3AICARKh/menBuayQUjAGPrqtHQQMUYERSdOrej92J9kz7LgYA==", + "license": "MMIT", + "dependencies": { + "readable-stream": "~1.0.26-2" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1010,6 +1544,46 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "license": "ISC", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "license": "MIT", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1021,6 +1595,42 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, + "node_modules/npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1038,6 +1648,18 @@ "node": ">= 0.8.0" } }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "license": "MIT", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1090,6 +1712,15 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1115,6 +1746,12 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1125,6 +1762,42 @@ "node": ">=6" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1135,6 +1808,54 @@ "node": ">=4" } }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1158,6 +1879,44 @@ "node": ">=8" } }, + "node_modules/splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==", + "license": "ISC" + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1180,7 +1939,34 @@ "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "license": "ISC", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" } }, "node_modules/type-check": { @@ -1202,6 +1988,65 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw==", + "license": "MIT", + "dependencies": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1212,6 +2057,18 @@ "punycode": "^2.1.0" } }, + "node_modules/url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1228,6 +2085,18 @@ "node": ">= 8" } }, + "node_modules/window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==", + "license": "MIT", + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -1237,6 +2106,52 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", + "license": "MIT", + "dependencies": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -1530,6 +2445,16 @@ "uri-js": "^4.2.2" } }, + "ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1539,34 +2464,138 @@ "color-convert": "^2.0.1" } }, + "are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==" + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==" + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1577,6 +2606,59 @@ "supports-color": "^7.1.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "cmake-js": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.2.tgz", + "integrity": "sha512-7MfiQ/ijzeE2kO+WFB9bv4QP5Dn2yVaAP2acFJr4NIFy2hT4w6O4EpOTLNcohR5IPX7M4wNf/5taIqMj7UA9ug==", + "requires": { + "axios": "^0.21.1", + "bluebird": "^3", + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1595,8 +2677,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "cross-spawn": { "version": "7.0.6", @@ -1617,12 +2703,69 @@ "ms": "2.1.2" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1755,6 +2898,11 @@ "flat-cache": "^4.0.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1781,6 +2929,70 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" + }, + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==", + "requires": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1796,12 +3008,22 @@ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1824,12 +3046,44 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "requires": { + "number-is-nan": "^1.0.0" + } + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1839,11 +3093,20 @@ "is-extglob": "^2.1.1" } }, + "is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha512-tLn1j3wYSL6DkvEI+V/j0pKohpa5jk+ER74v6S4SgCXnjS0WA+DoZbwZBrrhgwksMvtuwndyGeG5F8YMsoBzSA==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "js-tokens": { "version": "4.0.0", @@ -1882,6 +3145,14 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1891,6 +3162,14 @@ "json-buffer": "3.0.1" } }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "requires": { + "invert-kv": "^1.0.0" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1901,6 +3180,21 @@ "type-check": "~0.4.0" } }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, + "llvm-bindings": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/llvm-bindings/-/llvm-bindings-0.4.2.tgz", + "integrity": "sha512-WFFOeG23u1AkxSdJdbrPqxJtHKTlti8rEC2Hc5yvQyrGk352+Y844DZayhevPK0j+kzLpnJfuhqssKqZBmE1Sw==", + "requires": { + "bindings": "^1.5.0", + "cmake-js": "^6.3.2", + "node-addon-api": "^5.0.0" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1910,21 +3204,78 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==" + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==" + }, + "memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha512-q0D3m846qY6ZkIt+19ZemU5vH56lpOZZwoJc3AICARKh/menBuayQUjAGPrqtHQQMUYERSdOrej92J9kz7LgYA==", + "requires": { + "readable-stream": "~1.0.26-2" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1936,6 +3287,34 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==", + "requires": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, "optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1950,6 +3329,14 @@ "word-wrap": "^1.2.5" } }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "requires": { + "lcid": "^1.0.0" + } + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1983,6 +3370,11 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2000,18 +3392,75 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + } + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2027,6 +3476,34 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "requires": { + "ansi-regex": "^2.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2042,6 +3519,25 @@ "has-flag": "^4.0.0" } }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2057,6 +3553,58 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw==", + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + } + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2066,6 +3614,16 @@ "punycode": "^2.1.0" } }, + "url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2075,12 +3633,55 @@ "isexe": "^2.0.0" } }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==" + }, "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index ef6cc36..595aace 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@babel/generator": "7.28.5", "@babel/parser": "7.28.5", "@babel/traverse": "^7.28.5", - "@babel/types": "7.28.5" + "@babel/types": "7.28.5", + "llvm-bindings": "0.4.2" } } From f5be4ea11b85767ff7d009027bbe07c1a045d7be Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Fri, 26 Dec 2025 12:29:39 -0800 Subject: [PATCH 5/8] round out cli commands clean up readme --- README.md | 57 ++++++++++++++++++++++++++++++++++++-------- bin/cli.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5af8bab..96f4f9b 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ Complect Logo -Complect is a toy compiler developed in Node.js. It operates as an async generator-based compiler, processing code incrementally with push-like data flow for efficiency and modularity. Currently, Complect functions primarily as a transpiler, converting Complect code into JavaScript using the Babel Abstract Syntax Tree (AST). Future enhancements include support for switchable AST backends, enabling compilation to WebAssembly (WASM), custom interpreters, and other targets. +Complect is a toy compiler developed in Node.js. It operates as an async generator-based compiler, processing code incrementally with push-like data flow for efficiency and modularity. Complect supports multiple backends: transpilation to JavaScript using Babel AST, and compilation to LLVM IR for native code generation. The pluggable backend architecture enables easy addition of WebAssembly, custom interpreters, and other targets. The initial implementation of this compiler was created to support a talk I presented at **OpenJS World 2022**. You can find the contents of this talk here. [Slides](https://static.sched.com/hosted_files/openjsworld2022/78/OpenJSW%20World%202022.pdf) [Video](https://youtu.be/aPHf_-N2yTU) ## Stages - Preprocessor: Transforms an input stream into a sequence of preprocessing tokens yielded incrementally. - Tokenizer: Converts the sequence of preprocessing tokens into a sequence of tokens yielded incrementally. -- Abstract Syntax Tree (AST): Generates an AST from the sequence of tokens, currently utilizing Babel. -- Output: The Babel AST outputs code in JavaScript. +- Abstract Syntax Tree (AST): Generates an intermediate representation (IR) from the sequence of tokens. +- Output: Pluggable backends convert IR to JavaScript (via Babel AST) or LLVM IR for native compilation. ## Design Complect utilizes a handcrafted parser and lexer to give developers fine-grained control over the compilation process. The parser in Complect is a top-down parser of the LL(1) type, chosen for its simplicity and efficiency in parsing. @@ -22,18 +22,55 @@ Complect utilizes a handcrafted parser and lexer to give developers fine-grained - Async Generator-Based Compilation: The compiler processes code using async generators, yielding tokens and AST nodes incrementally for efficiency and modularity. This design supports asynchronous processing and easier debugging, with stages pushing data to the next via iteration (e.g., preprocessor yields tokens to tokenizer). While not a pure stream, it balances performance for large inputs with simplicity for a toy compiler. -- Babel AST Integration: Complect currently generates an Abstract Syntax Tree (AST) using Babel, which outputs JavaScript code. This integration with Babel allows leveraging its robust ecosystem to further process and transform the generated JavaScript code. +- LLVM IR Generation: Complect can generate LLVM Intermediate Representation for native code compilation using clang or llc. -- Modular AST Generation: The architecture supports pluggable backends, enabling output to JavaScript, WebAssembly, or custom interpreters. +- Modular AST Generation: The architecture supports pluggable backends via an intermediate representation (IR), enabling output to JavaScript, LLVM IR, WebAssembly, or custom interpreters. + +- Full Language Support: Both backends support the complete Complect language including integers, strings, arithmetic, concatenation, comparisons, and control flow. ### Future -- Support for switchable AST backends, including Binaryen for WebAssembly output and a custom AST for interpretation -- Modular AST generation via an intermediate representation (IR) to enable pluggable backends for JavaScript, WebAssembly, or custom interpreters -- Explore compiler optimization passes +- Additional backends: WebAssembly (Binaryen), custom interpreters +- Compiler optimization passes +- Enhanced language features ## Usage ### CLI -You can use Complect as a command-line tool. The entry point of the application is `cli.js`. It reads from the standard input, compiles the input, and writes the output to the standard output. +You can use Complect as a command-line tool. The entry point of the application is `bin/cli.js`. + +```bash +# Show help +complect --help + +# Compile from stdin to stdout (default: babel backend) +cat program | complect + +# Compile from file to stdout +complect --file program + +# Compile to specific backend +complect --file program --backend llvm + +# Compile to file +complect --file program --output program.js +complect --file program --backend llvm --output program.ll +``` + +#### LLVM IR Usage +When using the LLVM backend, Complect generates LLVM Intermediate Representation (.ll) files that can be compiled to native binaries: + +```bash +# Generate LLVM IR +complect --file fib --backend llvm --output fib.ll + +# Compile to assembly (for inspection) +llc fib.ll -o fib.s + +# Compile to executable binary +clang fib.ll -o fib + +# Run the binary +./fib +``` ### Testing Tests are written using Node's built-in test module. @@ -80,4 +117,4 @@ Complect is created by Jarrod Connolly. ## License MIT License -See [COPYING](COPYING) for the full license text. \ No newline at end of file +See [LICENSE](LICENSE) for the full license text. \ No newline at end of file diff --git a/bin/cli.js b/bin/cli.js index 054c4f2..4540245 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -6,17 +6,63 @@ */ import { compile } from '../lib/compiler.js'; import { pretty } from '../lib/util/pretty-hrtime.js'; +import { parseArgs } from 'node:util'; +import { createReadStream, createWriteStream } from 'node:fs'; const args = process.argv.slice(2); -let backend = 'babel'; +const parsedArgs = parseArgs({ + options: { + help: { + type: 'boolean', + short: 'h', + default: false, + }, + backend: { + type: 'string', + short: 'b', + default: 'babel', + }, + file: { + type: 'string', + short: 'f', + default: '', + }, + output: { + type: 'string', + short: 'o', + default: '', + } + }, +}); + +if (parsedArgs.values.help) { + console.log(`Complect Compiler CLI + +Usage: complect [options] + +Options: + -h, --help Show help information + -b, --backend Specify the backend to use (default: babel) + -f, --file Input source file (default: stdin) + -o, --output Output file (default: stdout) +`); + process.exit(0); +} -if (args.length > 0 && args[0] === '--backend') { - backend = args[1]; - // Remove the backend args - process.argv.splice(2, 2); +const backend = parsedArgs.values.backend; +const file = parsedArgs.values.file; +const output = parsedArgs.values.output; + +console.log(`Backend: ${backend}`); +if (file) { + console.log(`Compiling: ${file}`); +} +if (output) { + console.log(`Output: ${output}`); } -const inputStream = process.stdin; +const inputStream = file ? createReadStream(file) : process.stdin; +const outputStream = output ? createWriteStream(output) : process.stdout; const start = process.hrtime.bigint(); compile(inputStream, backend) @@ -25,12 +71,17 @@ compile(inputStream, backend) const totalTime = pretty(end - start); const message = `Total Time: ${totalTime} PreTokens: ${results.preprocessorTokenCount} Tokens: ${results.tokenCount} AST Nodes: ${results.astNodeCount} `; + if (backend === 'llvm') { - console.log(`; ${message}`); + outputStream.write(`; ${message}\n\n`); + outputStream.write(results.code); } else { - console.log(`// ${message}`); + outputStream.write(`// ${message}\n\n`); + outputStream.write(results.code); } - console.log(`\n${results.code}`); + + outputStream.end(); + }) .catch((err) => { console.error(err); From 1303e8b5c3d4784acdab37ee2e8d4d23a4375c79 Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Fri, 26 Dec 2025 12:47:17 -0800 Subject: [PATCH 6/8] lint errors --- bin/cli.js | 1 - lib/babel-ast/babel-translator.js | 2 +- lib/llvm/llvm-translator.js | 43 +------------------------------ 3 files changed, 2 insertions(+), 44 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 4540245..14712ef 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -9,7 +9,6 @@ import { pretty } from '../lib/util/pretty-hrtime.js'; import { parseArgs } from 'node:util'; import { createReadStream, createWriteStream } from 'node:fs'; -const args = process.argv.slice(2); const parsedArgs = parseArgs({ options: { help: { diff --git a/lib/babel-ast/babel-translator.js b/lib/babel-ast/babel-translator.js index cf0c7c8..d4623f4 100644 --- a/lib/babel-ast/babel-translator.js +++ b/lib/babel-ast/babel-translator.js @@ -9,7 +9,7 @@ import { CodeGenerator } from '@babel/generator'; import _traverse from "@babel/traverse"; const traverse = _traverse.default; -import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; +import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; export class BabelTranslator { translate(ir) { diff --git a/lib/llvm/llvm-translator.js b/lib/llvm/llvm-translator.js index 92f9fd6..24925d9 100644 --- a/lib/llvm/llvm-translator.js +++ b/lib/llvm/llvm-translator.js @@ -5,7 +5,7 @@ */ import llvm from 'llvm-bindings'; -import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; +import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; export class LLVMTranslator { constructor() { @@ -200,47 +200,6 @@ export class LLVMTranslator { } } - getExpressionType(expr) { - if (expr instanceof Identifier) { - const varInfo = this.variables.get(expr.name); - return varInfo ? varInfo.type : 'int'; // fallback - } else if (expr instanceof NumericLiteral) { - return 'int'; - } else if (expr instanceof StringLiteral) { - return 'string'; - } else if (expr instanceof BinaryExpression) { - // For binary expressions, we need to determine the result type - const leftType = this.getExpressionType(expr.left); - const rightType = this.getExpressionType(expr.right); - - if (expr.operator === '+') { - // String concatenation if either operand is string - if (leftType === 'string' || rightType === 'string') { - return 'string'; - } - } - - // For comparisons, result is always int (boolean) - if (['==', '!=', '<', '<=', '>', '>='].includes(expr.operator)) { - return 'int'; - } - - // Default to int for arithmetic - return 'int'; - } - return 'int'; - } - - getStringConstant(value) { - if (this.stringLiterals.has(value)) { - return this.stringLiterals.get(value); - } - - const strConst = this.builder.CreateGlobalStringPtr(value, `.str.${this.stringLiterals.size}`, 0, this.module); - this.stringLiterals.set(value, strConst); - return strConst; - } - stringConcat(left, right, leftType, rightType) { // Convert integers to strings if needed let leftStr = left; From 8751d28ade88775eddfc849dba206baf7dcdc8ee Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Fri, 26 Dec 2025 13:38:44 -0800 Subject: [PATCH 7/8] add simple arena memory manager, no free for now script to easily compile and run llvm backends --- .gitignore | 3 +- exec.sh | 11 +++ fixtures/fib | 2 +- fixtures/fizzbuzz | 2 +- lib/llvm/llvm-translator.js | 148 +++++++++++++++++++++++++++++++++++- 5 files changed, 162 insertions(+), 4 deletions(-) create mode 100755 exec.sh diff --git a/.gitignore b/.gitignore index 8fd3da1..bdd82e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules .vscode *.ll -*.s \ No newline at end of file +*.s +/output \ No newline at end of file diff --git a/exec.sh b/exec.sh new file mode 100755 index 0000000..04aae8a --- /dev/null +++ b/exec.sh @@ -0,0 +1,11 @@ +#! /bin/bash +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi +complect -b llvm -f fixtures/${1} -o output/${1}.ll +llc output/${1}.ll -o output/${1}.s +clang output/${1}.ll -s -o output/${1} +./output/${1} + + diff --git a/fixtures/fib b/fixtures/fib index 8e493af..30b40d0 100644 --- a/fixtures/fib +++ b/fixtures/fib @@ -1,7 +1,7 @@ make a 0 make b 1 make t 0 -make n 12 +make n 32 as n > 0 n = n - 1 assign t a diff --git a/fixtures/fizzbuzz b/fixtures/fizzbuzz index f1ebf0f..e2d334d 100644 --- a/fixtures/fizzbuzz +++ b/fixtures/fizzbuzz @@ -3,7 +3,7 @@ make f 0 make b 0 make output '' -as i <= 16 +as i <= 32 f = i % 3 b = i % 5 assign output '' diff --git a/lib/llvm/llvm-translator.js b/lib/llvm/llvm-translator.js index 24925d9..7a6a25c 100644 --- a/lib/llvm/llvm-translator.js +++ b/lib/llvm/llvm-translator.js @@ -15,6 +15,11 @@ export class LLVMTranslator { this.variables = new Map(); // name -> { type: 'int'|'string', value: LLVM Value } this.function = null; this.stringLiterals = new Map(); // value -> global string constant + + // Arena allocation + this.arenaPtr = null; // i8** - pointer to current arena position + this.freeList = []; // Array of {offset: LLVM Value, size: LLVM Value} + this.arenaSize = 64 * 1024; // 64KB arena } translate(ir) { @@ -22,6 +27,43 @@ export class LLVMTranslator { this.module.setTargetTriple('x86_64-pc-linux-gnu'); this.builder = new llvm.IRBuilder(this.context); + // Create globals for arena and free list + this.arenaBufferGlobal = new llvm.GlobalVariable( + this.module, + this.builder.getInt8PtrTy(), + false, + 3, // InternalLinkage + llvm.ConstantPointerNull.get(this.builder.getInt8PtrTy()), + 'arenaBuffer' + ); + + this.freeOffsetGlobal = new llvm.GlobalVariable( + this.module, + this.builder.getInt64Ty(), + false, + 3, + this.builder.getInt64(0), + 'freeOffset' + ); + + this.freeSizeGlobal = new llvm.GlobalVariable( + this.module, + this.builder.getInt64Ty(), + false, + 3, + this.builder.getInt64(0), + 'freeSize' + ); + + this.hasFreeGlobal = new llvm.GlobalVariable( + this.module, + this.builder.getInt32Ty(), + false, + 3, + this.builder.getInt32(0), + 'hasFree' + ); + // Declare runtime functions this.declareRuntimeFunctions(); @@ -32,6 +74,9 @@ export class LLVMTranslator { const entryBB = llvm.BasicBlock.Create(this.context, 'entry', this.function); this.builder.SetInsertPoint(entryBB); + // Initialize string arena + this.initializeArena(); + // Translate statements for (const stmt of ir.statements) { this.translateStatement(stmt); @@ -80,6 +125,107 @@ export class LLVMTranslator { this.module.getOrInsertFunction('strlen', strlenType); } + initializeArena() { + // Allocate arena buffer + const arenaBuffer = this.builder.CreateCall(this.module.getFunction('malloc'), [this.builder.getInt64(this.arenaSize)], 'arenaBuffer'); + + // Store in global + this.builder.CreateStore(arenaBuffer, this.arenaBufferGlobal); + + // Create arena pointer (i8**) + this.arenaPtr = this.builder.CreateAlloca(this.builder.getInt8PtrTy(), null, 'arenaPtr'); + + // Initialize to start of buffer + this.builder.CreateStore(arenaBuffer, this.arenaPtr); + } + + allocateFromArena(size) { + // Check if we have a free block + const hasFree = this.builder.CreateLoad(this.builder.getInt32Ty(), this.hasFreeGlobal, 'hasFree'); + const hasFreeBool = this.builder.CreateICmpNE(hasFree, this.builder.getInt32(0), 'hasFreeBool'); + + const freeBB = llvm.BasicBlock.Create(this.context, 'free_alloc', this.function); + const bumpBB = llvm.BasicBlock.Create(this.context, 'bump_alloc', this.function); + const mergeBB = llvm.BasicBlock.Create(this.context, 'alloc_merge', this.function); + + this.builder.CreateCondBr(hasFreeBool, freeBB, bumpBB); + + // Free block allocation + this.builder.SetInsertPoint(freeBB); + const freeSize = this.builder.CreateLoad(this.builder.getInt64Ty(), this.freeSizeGlobal, 'freeSize'); + const fits = this.builder.CreateICmpUGE(freeSize, size, 'fits'); + const useFreeBB = llvm.BasicBlock.Create(this.context, 'use_free', this.function); + const noFitBB = llvm.BasicBlock.Create(this.context, 'no_fit', this.function); + this.builder.CreateCondBr(fits, useFreeBB, noFitBB); + + this.builder.SetInsertPoint(useFreeBB); + const freeOffset = this.builder.CreateLoad(this.builder.getInt64Ty(), this.freeOffsetGlobal, 'freeOffset'); + const arenaBuffer = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaBufferGlobal, 'arenaBuffer'); + const freePtr = this.builder.CreateGEP(this.builder.getInt8Ty(), arenaBuffer, [freeOffset], 'freePtr'); + // Clear free block + this.builder.CreateStore(this.builder.getInt32(0), this.hasFreeGlobal); + this.builder.CreateBr(mergeBB); + + this.builder.SetInsertPoint(noFitBB); + this.builder.CreateBr(bumpBB); + + // Bump allocation + this.builder.SetInsertPoint(bumpBB); + const bumpPtr = this.bumpAllocate(size); + this.builder.CreateBr(mergeBB); + + // Merge + this.builder.SetInsertPoint(mergeBB); + const phi = this.builder.CreatePHI(this.builder.getInt8PtrTy(), 2, 'allocPtr'); + phi.addIncoming(freePtr, useFreeBB); + phi.addIncoming(bumpPtr, bumpBB); + + return phi; + } + + alignSize(size) { + // For now, no alignment to debug + return size; + } + + findFreeBlock(requestedSize) { + // For basic implementation, always return null (use bump allocation) + // Free list implementation would require complex runtime data structures + return null; + } + + allocateFromFreeBlock(block, size) { + // Placeholder - not implemented in basic version + // Would need complex LLVM IR for runtime free list management + return this.bumpAllocate(size); + } + + bumpAllocate(size) { + // Get current arena position + const currentPtr = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaPtr, 'currentPtr'); + + // Calculate new position + const newPtr = this.builder.CreateGEP(this.builder.getInt8Ty(), currentPtr, [size], 'newPtr'); + + // Store new position + this.builder.CreateStore(newPtr, this.arenaPtr); + + // Return current pointer as allocated block + return currentPtr; + } + + deallocateToArena(ptr, size) { + // For now, just set the free block (no merging) + const arenaBuffer = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaBufferGlobal, 'arenaBuffer'); + const ptrInt = this.builder.CreatePtrToInt(ptr, this.builder.getInt64Ty(), 'ptrInt'); + const baseInt = this.builder.CreatePtrToInt(arenaBuffer, this.builder.getInt64Ty(), 'baseInt'); + const offset = this.builder.CreateSub(ptrInt, baseInt, 'offset'); + + this.builder.CreateStore(offset, this.freeOffsetGlobal); + this.builder.CreateStore(size, this.freeSizeGlobal); + this.builder.CreateStore(this.builder.getInt32(1), this.hasFreeGlobal); + } + translateStatement(stmt) { if (stmt instanceof VariableDeclaration) { const value = this.translateExpression(stmt.value); @@ -242,7 +388,7 @@ export class LLVMTranslator { intToString(value) { // Allocate buffer for number string (up to 32 digits should be enough) - const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [this.builder.getInt64(32)], 'intbuf'); + const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [this.builder.getInt64(32)], 'buffer'); const formatStr = this.getStringConstant('%d'); this.builder.CreateCall(this.module.getFunction('sprintf'), [buffer, formatStr, value], 'sprintf'); return buffer; From 4542090c342786d095d0e2a10e774260da8888d8 Mon Sep 17 00:00:00 2001 From: Jarrod Connolly Date: Fri, 26 Dec 2025 15:17:21 -0800 Subject: [PATCH 8/8] add explicit free command add auto free on string assignment add free temp variable in expressions --- exec.sh | 2 +- fixtures/fizzbuzz | 4 +- lib/ast/ast-builder.js | 6 +- lib/ast/ir-nodes.js | 8 ++ lib/babel-ast/babel-translator.js | 11 +- lib/llvm/llvm-translator.js | 204 ++++++++---------------------- lib/tokenizer/keywords.js | 1 + 7 files changed, 81 insertions(+), 155 deletions(-) diff --git a/exec.sh b/exec.sh index 04aae8a..b0feae0 100755 --- a/exec.sh +++ b/exec.sh @@ -7,5 +7,5 @@ complect -b llvm -f fixtures/${1} -o output/${1}.ll llc output/${1}.ll -o output/${1}.s clang output/${1}.ll -s -o output/${1} ./output/${1} - +valgrind --tool=memcheck --leak-check=full ./output/${1} 2>&1 | grep -E "(allocs| frees| allocated| lost)" diff --git a/fixtures/fizzbuzz b/fixtures/fizzbuzz index e2d334d..05f4104 100644 --- a/fixtures/fizzbuzz +++ b/fixtures/fizzbuzz @@ -3,10 +3,10 @@ make f 0 make b 0 make output '' -as i <= 32 +as i <= 1024 + assign output '' f = i % 3 b = i % 5 - assign output '' if f == 0 output = output + 'Fizz' endif diff --git a/lib/ast/ast-builder.js b/lib/ast/ast-builder.js index 393b299..b9a1cf9 100644 --- a/lib/ast/ast-builder.js +++ b/lib/ast/ast-builder.js @@ -5,7 +5,7 @@ */ import { TokenType } from '../tokenizer/token-type.js'; -import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from './ir-nodes.js'; +import { Program, VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, FreeStatement, Identifier, NumericLiteral, StringLiteral } from './ir-nodes.js'; // ASTBuilder builds an IR AST from tokens using a state machine export class ASTBuilder { @@ -59,6 +59,10 @@ export class ASTBuilder { } else if (token.value === 'print') { const expr = this.parseFullExpression(); return new PrintStatement(expr, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); + } else if (token.value === 'free') { + const idToken = this.tokens[this.index++]; + if (idToken.type !== TokenType.identifier) throw new Error(`Expected identifier after 'free'`); + return new FreeStatement(idToken.value, { start: { line: token.line, column: token.column }, end: { line: token.line, column: token.column } }); } else if (token.value === 'if') { const test = this.parseFullExpression(); const consequent = this.parseBlock('endif'); diff --git a/lib/ast/ir-nodes.js b/lib/ast/ir-nodes.js index 2357e58..37ced17 100644 --- a/lib/ast/ir-nodes.js +++ b/lib/ast/ir-nodes.js @@ -87,6 +87,14 @@ export class PrintStatement extends Statement { } } +// Free statement (e.g., free x) +export class FreeStatement extends Statement { + constructor(identifier, loc) { + super(loc); + this.identifier = identifier; // string (variable name) + } +} + // Identifier (e.g., variable name) export class Identifier extends Expression { constructor(name, loc) { diff --git a/lib/babel-ast/babel-translator.js b/lib/babel-ast/babel-translator.js index d4623f4..bee25b2 100644 --- a/lib/babel-ast/babel-translator.js +++ b/lib/babel-ast/babel-translator.js @@ -9,7 +9,7 @@ import { CodeGenerator } from '@babel/generator'; import _traverse from "@babel/traverse"; const traverse = _traverse.default; -import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; +import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, FreeStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; export class BabelTranslator { translate(ir) { @@ -74,6 +74,15 @@ export class BabelTranslator { const exprStmt = t.expressionStatement(call); exprStmt.loc = stmt.loc; return exprStmt; + } else if (stmt instanceof FreeStatement) { + // In JavaScript, we help GC by setting to null + const id = t.identifier(stmt.identifier); + id.loc = stmt.loc; + const nullLit = t.nullLiteral(); + const assign = t.assignmentExpression('=', id, nullLit); + const exprStmt = t.expressionStatement(assign); + exprStmt.loc = stmt.loc; + return exprStmt; } else { throw new Error(`Unknown statement type: ${stmt.constructor.name}`); } diff --git a/lib/llvm/llvm-translator.js b/lib/llvm/llvm-translator.js index 7a6a25c..e1e92a8 100644 --- a/lib/llvm/llvm-translator.js +++ b/lib/llvm/llvm-translator.js @@ -5,7 +5,7 @@ */ import llvm from 'llvm-bindings'; -import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; +import { VariableDeclaration, AssignmentExpression, BinaryExpression, IfStatement, WhileStatement, PrintStatement, FreeStatement, Identifier, NumericLiteral, StringLiteral } from '../ast/ir-nodes.js'; export class LLVMTranslator { constructor() { @@ -15,11 +15,6 @@ export class LLVMTranslator { this.variables = new Map(); // name -> { type: 'int'|'string', value: LLVM Value } this.function = null; this.stringLiterals = new Map(); // value -> global string constant - - // Arena allocation - this.arenaPtr = null; // i8** - pointer to current arena position - this.freeList = []; // Array of {offset: LLVM Value, size: LLVM Value} - this.arenaSize = 64 * 1024; // 64KB arena } translate(ir) { @@ -27,43 +22,6 @@ export class LLVMTranslator { this.module.setTargetTriple('x86_64-pc-linux-gnu'); this.builder = new llvm.IRBuilder(this.context); - // Create globals for arena and free list - this.arenaBufferGlobal = new llvm.GlobalVariable( - this.module, - this.builder.getInt8PtrTy(), - false, - 3, // InternalLinkage - llvm.ConstantPointerNull.get(this.builder.getInt8PtrTy()), - 'arenaBuffer' - ); - - this.freeOffsetGlobal = new llvm.GlobalVariable( - this.module, - this.builder.getInt64Ty(), - false, - 3, - this.builder.getInt64(0), - 'freeOffset' - ); - - this.freeSizeGlobal = new llvm.GlobalVariable( - this.module, - this.builder.getInt64Ty(), - false, - 3, - this.builder.getInt64(0), - 'freeSize' - ); - - this.hasFreeGlobal = new llvm.GlobalVariable( - this.module, - this.builder.getInt32Ty(), - false, - 3, - this.builder.getInt32(0), - 'hasFree' - ); - // Declare runtime functions this.declareRuntimeFunctions(); @@ -74,9 +32,6 @@ export class LLVMTranslator { const entryBB = llvm.BasicBlock.Create(this.context, 'entry', this.function); this.builder.SetInsertPoint(entryBB); - // Initialize string arena - this.initializeArena(); - // Translate statements for (const stmt of ir.statements) { this.translateStatement(stmt); @@ -109,6 +64,9 @@ export class LLVMTranslator { const mallocType = llvm.FunctionType.get(this.builder.getInt8PtrTy(), [this.builder.getInt64Ty()], false); this.module.getOrInsertFunction('malloc', mallocType); + const freeType = llvm.FunctionType.get(this.builder.getVoidTy(), [this.builder.getInt8PtrTy()], false); + this.module.getOrInsertFunction('free', freeType); + const strcpyType = llvm.FunctionType.get(this.builder.getInt8PtrTy(), [this.builder.getInt8PtrTy(), this.builder.getInt8PtrTy()], false); this.module.getOrInsertFunction('strcpy', strcpyType); @@ -125,107 +83,6 @@ export class LLVMTranslator { this.module.getOrInsertFunction('strlen', strlenType); } - initializeArena() { - // Allocate arena buffer - const arenaBuffer = this.builder.CreateCall(this.module.getFunction('malloc'), [this.builder.getInt64(this.arenaSize)], 'arenaBuffer'); - - // Store in global - this.builder.CreateStore(arenaBuffer, this.arenaBufferGlobal); - - // Create arena pointer (i8**) - this.arenaPtr = this.builder.CreateAlloca(this.builder.getInt8PtrTy(), null, 'arenaPtr'); - - // Initialize to start of buffer - this.builder.CreateStore(arenaBuffer, this.arenaPtr); - } - - allocateFromArena(size) { - // Check if we have a free block - const hasFree = this.builder.CreateLoad(this.builder.getInt32Ty(), this.hasFreeGlobal, 'hasFree'); - const hasFreeBool = this.builder.CreateICmpNE(hasFree, this.builder.getInt32(0), 'hasFreeBool'); - - const freeBB = llvm.BasicBlock.Create(this.context, 'free_alloc', this.function); - const bumpBB = llvm.BasicBlock.Create(this.context, 'bump_alloc', this.function); - const mergeBB = llvm.BasicBlock.Create(this.context, 'alloc_merge', this.function); - - this.builder.CreateCondBr(hasFreeBool, freeBB, bumpBB); - - // Free block allocation - this.builder.SetInsertPoint(freeBB); - const freeSize = this.builder.CreateLoad(this.builder.getInt64Ty(), this.freeSizeGlobal, 'freeSize'); - const fits = this.builder.CreateICmpUGE(freeSize, size, 'fits'); - const useFreeBB = llvm.BasicBlock.Create(this.context, 'use_free', this.function); - const noFitBB = llvm.BasicBlock.Create(this.context, 'no_fit', this.function); - this.builder.CreateCondBr(fits, useFreeBB, noFitBB); - - this.builder.SetInsertPoint(useFreeBB); - const freeOffset = this.builder.CreateLoad(this.builder.getInt64Ty(), this.freeOffsetGlobal, 'freeOffset'); - const arenaBuffer = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaBufferGlobal, 'arenaBuffer'); - const freePtr = this.builder.CreateGEP(this.builder.getInt8Ty(), arenaBuffer, [freeOffset], 'freePtr'); - // Clear free block - this.builder.CreateStore(this.builder.getInt32(0), this.hasFreeGlobal); - this.builder.CreateBr(mergeBB); - - this.builder.SetInsertPoint(noFitBB); - this.builder.CreateBr(bumpBB); - - // Bump allocation - this.builder.SetInsertPoint(bumpBB); - const bumpPtr = this.bumpAllocate(size); - this.builder.CreateBr(mergeBB); - - // Merge - this.builder.SetInsertPoint(mergeBB); - const phi = this.builder.CreatePHI(this.builder.getInt8PtrTy(), 2, 'allocPtr'); - phi.addIncoming(freePtr, useFreeBB); - phi.addIncoming(bumpPtr, bumpBB); - - return phi; - } - - alignSize(size) { - // For now, no alignment to debug - return size; - } - - findFreeBlock(requestedSize) { - // For basic implementation, always return null (use bump allocation) - // Free list implementation would require complex runtime data structures - return null; - } - - allocateFromFreeBlock(block, size) { - // Placeholder - not implemented in basic version - // Would need complex LLVM IR for runtime free list management - return this.bumpAllocate(size); - } - - bumpAllocate(size) { - // Get current arena position - const currentPtr = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaPtr, 'currentPtr'); - - // Calculate new position - const newPtr = this.builder.CreateGEP(this.builder.getInt8Ty(), currentPtr, [size], 'newPtr'); - - // Store new position - this.builder.CreateStore(newPtr, this.arenaPtr); - - // Return current pointer as allocated block - return currentPtr; - } - - deallocateToArena(ptr, size) { - // For now, just set the free block (no merging) - const arenaBuffer = this.builder.CreateLoad(this.builder.getInt8PtrTy(), this.arenaBufferGlobal, 'arenaBuffer'); - const ptrInt = this.builder.CreatePtrToInt(ptr, this.builder.getInt64Ty(), 'ptrInt'); - const baseInt = this.builder.CreatePtrToInt(arenaBuffer, this.builder.getInt64Ty(), 'baseInt'); - const offset = this.builder.CreateSub(ptrInt, baseInt, 'offset'); - - this.builder.CreateStore(offset, this.freeOffsetGlobal); - this.builder.CreateStore(size, this.freeSizeGlobal); - this.builder.CreateStore(this.builder.getInt32(1), this.hasFreeGlobal); - } - translateStatement(stmt) { if (stmt instanceof VariableDeclaration) { const value = this.translateExpression(stmt.value); @@ -239,23 +96,62 @@ export class LLVMTranslator { } else if (varInfo.type === 'string') { // Create alloca for string variable (i8**) const alloca = this.builder.CreateAlloca(this.builder.getInt8PtrTy(), null, stmt.identifier); - this.builder.CreateStore(value, alloca); + + // For string variables, allocate a copy of the initial value + if (stmt.value instanceof StringLiteral) { + const strLen = this.builder.CreateCall(this.module.getFunction('strlen'), [value], 'strlen'); + const allocSize = this.builder.CreateAdd(strLen, this.builder.getInt64(1), 'allocSize'); + const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [allocSize], 'buffer'); + this.builder.CreateCall(this.module.getFunction('strcpy'), [buffer, value], 'strcpy'); + this.builder.CreateStore(buffer, alloca); + } else { + this.builder.CreateStore(value, alloca); + } + varInfo.value = alloca; } this.variables.set(stmt.identifier, varInfo); } else if (stmt instanceof AssignmentExpression) { - const value = this.translateExpression(stmt.right); + const rightExpr = stmt.right; + const value = this.translateExpression(rightExpr); const varInfo = this.variables.get(stmt.left); if (!varInfo) { throw new Error(`Undefined variable: ${stmt.left}`); } - this.builder.CreateStore(value, varInfo.value); + + // For string assignments, ensure we store a malloc'd copy + let finalValue = value; + if (varInfo.type === 'string' && rightExpr instanceof StringLiteral) { + const strLen = this.builder.CreateCall(this.module.getFunction('strlen'), [value], 'strlen'); + const allocSize = this.builder.CreateAdd(strLen, this.builder.getInt64(1), 'allocSize'); + const buffer = this.builder.CreateCall(this.module.getFunction('malloc'), [allocSize], 'buffer'); + this.builder.CreateCall(this.module.getFunction('strcpy'), [buffer, value], 'strcpy'); + finalValue = buffer; + } + + // Automatically free old string value before assignment + if (varInfo.type === 'string') { + const oldValue = this.builder.CreateLoad(this.builder.getInt8PtrTy(), varInfo.value); + this.builder.CreateCall(this.module.getFunction('free'), [oldValue]); + } + + this.builder.CreateStore(finalValue, varInfo.value); } else if (stmt instanceof PrintStatement) { this.translatePrint(stmt); + } else if (stmt instanceof FreeStatement) { + const varInfo = this.variables.get(stmt.identifier); + if (!varInfo) { + throw new Error(`Undefined variable: ${stmt.identifier}`); + } + if (varInfo.type === 'string') { + const oldValue = this.builder.CreateLoad(this.builder.getInt8PtrTy(), varInfo.value); + this.builder.CreateCall(this.module.getFunction('free'), [oldValue]); + } + } else if (stmt instanceof IfStatement) { this.translateIf(stmt); @@ -369,6 +265,14 @@ export class LLVMTranslator { // Concatenate second string this.builder.CreateCall(this.module.getFunction('strcat'), [buffer, rightStr], 'concat'); + // Free temporaries created by intToString + if (leftType === 'int') { + this.builder.CreateCall(this.module.getFunction('free'), [leftStr]); + } + if (rightType === 'int') { + this.builder.CreateCall(this.module.getFunction('free'), [rightStr]); + } + return buffer; } diff --git a/lib/tokenizer/keywords.js b/lib/tokenizer/keywords.js index ecb1bcd..c32357d 100644 --- a/lib/tokenizer/keywords.js +++ b/lib/tokenizer/keywords.js @@ -14,4 +14,5 @@ export const keywords = [ 'as', 'repeat', 'print', + 'free', ];