From cf38054c7e6bfb1723656e9e03ceec3a660d363f Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Sat, 18 Oct 2025 12:07:47 +0200 Subject: [PATCH 01/17] Create doc rule --- .../linter/docs/rules/html-turbo-permanent.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 javascript/packages/linter/docs/rules/html-turbo-permanent.md diff --git a/javascript/packages/linter/docs/rules/html-turbo-permanent.md b/javascript/packages/linter/docs/rules/html-turbo-permanent.md new file mode 100644 index 000000000..09e8832a7 --- /dev/null +++ b/javascript/packages/linter/docs/rules/html-turbo-permanent.md @@ -0,0 +1,32 @@ +# Linter Rule: HTML Turbo permanent attribute usage + +**Rule:** `html-turbo-permanent` + +## Description + +Ensure that turbo permanent attributes are used correctly in HTML elements. The `data-turbo-permanent` attribute is used to mark elements that should persist across page navigations in Turbo Drive. + +## Rationale + +Data turbo permanent is active if the attribute is present, `data-turbo-permanent="false"` behaves the same as `data-turbo-permanent`. This should be disallowed to avoid confusion. + +## Examples + +### ✅ Good + +```html +
1 item
+ +
1 item
+``` + +### 🚫 Bad + +```html +
1 item
+``` + +## References + +* [Turbo: `data-turbo-permanent` attribute](https://turbo.hotwired.dev/handbook/drive#permanent-elements) +* [HTML: Boolean Attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) From 1164af62b8b349ebff0b7170daf8f4d1fce529a5 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Sat, 18 Oct 2025 12:17:22 +0200 Subject: [PATCH 02/17] Implement draft --- .../linter/src/rules/html-turbo-permanent.ts | 31 +++++++++++++++++++ .../test/rules/html-turbo-permanent.test.ts | 24 ++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 javascript/packages/linter/src/rules/html-turbo-permanent.ts create mode 100644 javascript/packages/linter/test/rules/html-turbo-permanent.test.ts diff --git a/javascript/packages/linter/src/rules/html-turbo-permanent.ts b/javascript/packages/linter/src/rules/html-turbo-permanent.ts new file mode 100644 index 000000000..de186859f --- /dev/null +++ b/javascript/packages/linter/src/rules/html-turbo-permanent.ts @@ -0,0 +1,31 @@ +import { BaseRuleVisitor, getTagName, hasAttribute } from "./rule-utils.js" + +import { ParserRule } from "../types.js" +import type { LintOffense, LintContext } from "../types.js" +import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" + +class HTMLTurboPermanentVisitor extends BaseRuleVisitor { + visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { + this.checkTurboPermanentAttribute(node) + super.visitHTMLOpenTagNode(node) + } + + private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { + if (hasAttribute(node, "data-turbo-permanent") && getAttributeValue(node, "data-turbo-permanent") == "true") { + this.addOffense( + 'Attribute `data-turbo-permanent` should not contain value "false"', + node.tag_name!.location, + "error" + ) + } + } + +export class HTMLTurboPermanentRule extends ParserRule { + name = "html-turbo-permanent" + + check(result: ParseResult, context?: Partial): LintOffense[] { + const visitor = new HTMLTurboPermanentVisitor(this.name, context) + visitor.visit(result.value) + return visitor.offenses + } +} diff --git a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts new file mode 100644 index 000000000..f64b347be --- /dev/null +++ b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts @@ -0,0 +1,24 @@ +import { describe, test } from "vitest" +import { HTMLImgRequireAltRule } from "../../src/rules/html-turbo-permanent.js" +import { createLinterTest } from "../helpers/linter-test-helper.js" + +const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HTMLTurboPermanentRule) + +describe("html-tubro-permanent", () => { + test("passes when no explicit value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("passes when empty explicit value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("passes when string true value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("fails when passing permanent=false", () => { + expectError('Attribute `data-turbo-permanent` should not contain value "false"') + assertOffenses('
1 item
') + }) +}) From 9e169488be0c875cbd114329c15ea04a1f5c7f93 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Sat, 18 Oct 2025 12:27:49 +0200 Subject: [PATCH 03/17] Update import --- javascript/packages/linter/src/rules/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/packages/linter/src/rules/index.ts b/javascript/packages/linter/src/rules/index.ts index 815093506..42921a9cb 100644 --- a/javascript/packages/linter/src/rules/index.ts +++ b/javascript/packages/linter/src/rules/index.ts @@ -59,6 +59,7 @@ export * from "./herb-disable-comment-valid-rule-name.js" export * from "./html-allowed-script-type.js" export * from "./html-anchor-require-href.js" +export * from "./html-turbo-permanent.js" export * from "./html-aria-label-is-well-formatted.js" export * from "./html-aria-level-must-be-valid.js" export * from "./html-aria-role-heading-requires-level.js" From 13e13c9eab6106187325922dfadd635760aa8eed Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 13:54:15 +0100 Subject: [PATCH 04/17] Refactorings --- .../packages/linter/src/rules/html-turbo-permanent.ts | 8 ++++++-- .../linter/test/rules/html-turbo-permanent.test.ts | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/javascript/packages/linter/src/rules/html-turbo-permanent.ts b/javascript/packages/linter/src/rules/html-turbo-permanent.ts index de186859f..d541d2243 100644 --- a/javascript/packages/linter/src/rules/html-turbo-permanent.ts +++ b/javascript/packages/linter/src/rules/html-turbo-permanent.ts @@ -1,4 +1,4 @@ -import { BaseRuleVisitor, getTagName, hasAttribute } from "./rule-utils.js" +import { BaseRuleVisitor, getTagName, hasAttribute, getAttributeValue, findAttributeByName, getAttributes } from "./rule-utils.js" import { ParserRule } from "../types.js" import type { LintOffense, LintContext } from "../types.js" @@ -11,7 +11,10 @@ class HTMLTurboPermanentVisitor extends BaseRuleVisitor { } private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { - if (hasAttribute(node, "data-turbo-permanent") && getAttributeValue(node, "data-turbo-permanent") == "true") { + const attribute = findAttributeByName(getAttributes(node), "data-turbo-permanent") + if (!attribute) return + + if (getAttributeValue(attribute) === "true") { this.addOffense( 'Attribute `data-turbo-permanent` should not contain value "false"', node.tag_name!.location, @@ -19,6 +22,7 @@ class HTMLTurboPermanentVisitor extends BaseRuleVisitor { ) } } +} export class HTMLTurboPermanentRule extends ParserRule { name = "html-turbo-permanent" diff --git a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts index f64b347be..935d8a13d 100644 --- a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts +++ b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts @@ -1,5 +1,5 @@ import { describe, test } from "vitest" -import { HTMLImgRequireAltRule } from "../../src/rules/html-turbo-permanent.js" +import { HTMLTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" import { createLinterTest } from "../helpers/linter-test-helper.js" const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HTMLTurboPermanentRule) @@ -10,7 +10,8 @@ describe("html-tubro-permanent", () => { }) test("passes when empty explicit value is given", () => { - expectNoOffenses('
1 item
') + expectError('') + assertOffenses('
1 item
') }) test("passes when string true value is given", () => { From 24993a98683fbe9933ca1fb13253de464113fb81 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 14:24:28 +0100 Subject: [PATCH 05/17] Wip add boilerplate --- javascript/packages/turbo-lint/README.md | 39 +++++++++ .../packages/turbo-lint/docs/rules/README.md | 7 ++ .../docs/rules/html-turbo-permanent.md | 32 +++++++ javascript/packages/turbo-lint/package.json | 50 +++++++++++ javascript/packages/turbo-lint/project.json | 33 +++++++ .../packages/turbo-lint/rollup.config.mjs | 85 +++++++++++++++++++ javascript/packages/turbo-lint/tsconfig.json | 20 +++++ 7 files changed, 266 insertions(+) create mode 100644 javascript/packages/turbo-lint/README.md create mode 100644 javascript/packages/turbo-lint/docs/rules/README.md create mode 100644 javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md create mode 100644 javascript/packages/turbo-lint/package.json create mode 100644 javascript/packages/turbo-lint/project.json create mode 100644 javascript/packages/turbo-lint/rollup.config.mjs create mode 100644 javascript/packages/turbo-lint/tsconfig.json diff --git a/javascript/packages/turbo-lint/README.md b/javascript/packages/turbo-lint/README.md new file mode 100644 index 000000000..5babef305 --- /dev/null +++ b/javascript/packages/turbo-lint/README.md @@ -0,0 +1,39 @@ +# Turbo Lint + +**Package**: [`turbo-lint`](https://www.npmjs.com/package/turbo-lint) + +--- + +Linter and static code analyzer for [Turbo](https://turbo.hotwired.dev/), powered by [Herb](https://github.com/marcoroth/herb). + +### Installation + +:::code-group + +```shell [npm] +npm install turbo-lint +``` + +```shell [yarn] +yarn add turbo-lint +``` + +```shell [pnpm] +pnpm add turbo-lint +``` + +```shell [bun] +bun add turbo-lint +``` +::: + + +### Run + +```bash +npx turbo-lint +``` + +## Rules + +Rules are documented [here](./docs/rules/) diff --git a/javascript/packages/turbo-lint/docs/rules/README.md b/javascript/packages/turbo-lint/docs/rules/README.md new file mode 100644 index 000000000..b28b55ce8 --- /dev/null +++ b/javascript/packages/turbo-lint/docs/rules/README.md @@ -0,0 +1,7 @@ +# Linter Rules + +This page contains documentation for all Turbo Linter rules. + +## Available Rules + +- [`turbo-permanent`](./html-turbo-permanent.md) - Prevents invalid usage of the boolean attribute diff --git a/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md b/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md new file mode 100644 index 000000000..09e8832a7 --- /dev/null +++ b/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md @@ -0,0 +1,32 @@ +# Linter Rule: HTML Turbo permanent attribute usage + +**Rule:** `html-turbo-permanent` + +## Description + +Ensure that turbo permanent attributes are used correctly in HTML elements. The `data-turbo-permanent` attribute is used to mark elements that should persist across page navigations in Turbo Drive. + +## Rationale + +Data turbo permanent is active if the attribute is present, `data-turbo-permanent="false"` behaves the same as `data-turbo-permanent`. This should be disallowed to avoid confusion. + +## Examples + +### ✅ Good + +```html +
1 item
+ +
1 item
+``` + +### 🚫 Bad + +```html +
1 item
+``` + +## References + +* [Turbo: `data-turbo-permanent` attribute](https://turbo.hotwired.dev/handbook/drive#permanent-elements) +* [HTML: Boolean Attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) diff --git a/javascript/packages/turbo-lint/package.json b/javascript/packages/turbo-lint/package.json new file mode 100644 index 000000000..76250a1fb --- /dev/null +++ b/javascript/packages/turbo-lint/package.json @@ -0,0 +1,50 @@ +{ + "name": "turbo-lint", + "version": "0.1.0", + "description": "Linting rules for Turbo HTML+ERB view templates.", + "license": "MIT", + "homepage": "https://herb-tools.dev", + "bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60turbo-lint%60:%20", + "repository": { + "type": "git", + "url": "https://github.com/marcoroth/herb.git", + "directory": "javascript/packages/turbo-lint" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "bin": { + "turbo-lint": "./bin/turbo-lint" + }, + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "default": "./dist/index.js" + } + }, + "scripts": { + "clean": "rimraf dist", + "build": "yarn clean && tsc -b && rollup -c", + "watch": "tsc -b -w", + "test": "vitest run", + "test:watch": "vitest --watch", + "prepublishOnly": "yarn clean && yarn build && yarn test" + }, + "dependencies": { + "@herb-tools/core": "0.7.5", + "@herb-tools/highlighter": "0.7.5", + "@herb-tools/linter": "0.7.5", + "@herb-tools/node-wasm": "0.7.5" + }, + "files": [ + "package.json", + "README.md", + "docs/", + "src/", + "bin/", + "dist/" + ] +} diff --git a/javascript/packages/turbo-lint/project.json b/javascript/packages/turbo-lint/project.json new file mode 100644 index 000000000..897c4e95c --- /dev/null +++ b/javascript/packages/turbo-lint/project.json @@ -0,0 +1,33 @@ +{ + "name": "turbo-lint", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "javascript/packages/turbo-lint/src", + "projectType": "library", + "targets": { + "build": { + "executor": "nx:run-script", + "options": { + "script": "build" + }, + "dependsOn": [ + "@herb-tools/core:build", + "@herb-tools/node-wasm:build", + "@herb-tools/linter:build", + "@herb-tools/highlighter:build" + ] + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + }, + "clean": { + "executor": "nx:run-script", + "options": { + "script": "clean" + } + } + }, + "tags": [] +} diff --git a/javascript/packages/turbo-lint/rollup.config.mjs b/javascript/packages/turbo-lint/rollup.config.mjs new file mode 100644 index 000000000..32bbe5254 --- /dev/null +++ b/javascript/packages/turbo-lint/rollup.config.mjs @@ -0,0 +1,85 @@ +import typescript from "@rollup/plugin-typescript" +import { nodeResolve } from "@rollup/plugin-node-resolve" +import json from "@rollup/plugin-json" +import commonjs from "@rollup/plugin-commonjs" + +// Bundle the CLI entry point into a single CommonJS file. +// Exclude Node built-in so they remain as externals. +const external = [ + "path", + "url", + "fs", + "module", +] + +function isExternal(id) { + return ( + external.includes(id) || + external.some((pkg) => id === pkg || id.startsWith(pkg + "/")) + ) +} + +export default [ + // CLI entry point (CommonJS) + { + input: "src/turbo-lint.ts", + output: { + file: "dist/turbo-lint.js", + format: "cjs", + sourcemap: true, + }, + external: isExternal, + plugins: [ + nodeResolve(), + commonjs(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + rootDir: "src/", + module: "esnext", + }), + ], + }, + + // Library exports (ESM) + { + input: "src/index.ts", + output: { + file: "dist/index.js", + format: "esm", + sourcemap: true, + }, + external: ["@herb-tools/core", "@herb-tools/linter", "@herb-tools/node-wasm", "turbo-parser"], + plugins: [ + nodeResolve(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + declaration: true, + declarationDir: "./dist/types", + rootDir: "src/", + }), + ], + }, + + // Library exports (CommonJS) + { + input: "src/index.ts", + external: ["@herb-tools/core", "@herb-tools/linter", "@herb-tools/node-wasm", "turbo-parser"], + output: { + file: "dist/index.cjs", + format: "cjs", + sourcemap: true, + }, + plugins: [ + nodeResolve(), + commonjs(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + rootDir: "src/", + module: "esnext", + }), + ], + }, +] diff --git a/javascript/packages/turbo-lint/tsconfig.json b/javascript/packages/turbo-lint/tsconfig.json new file mode 100644 index 000000000..b36bf65cd --- /dev/null +++ b/javascript/packages/turbo-lint/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022"], + "outDir": "./dist", + "declaration": true, + "declarationDir": "./dist/types", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "sourceMap": true + }, + "include": ["src/**/*", "types/**/*"], + "exclude": ["node_modules", "dist"] +} From b403310a53ac7a47521db8268f151a15280b0615 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 14:29:48 +0100 Subject: [PATCH 06/17] Add javascript workspace --- .github/labeler.yml | 5 +++++ workspace.json | 1 + 2 files changed, 6 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index c73114eae..27b9e931e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -173,6 +173,11 @@ node: - any-glob-to-any-file: - '**/javascript/packages/node/**/*' +turbo-lint: + - changed-files: + - any-glob-to-any-file: + - '**/javascript/packages/turbo-lint/**/*' + stimulus-lint: - changed-files: - any-glob-to-any-file: diff --git a/workspace.json b/workspace.json index 27648b191..e95cd9f0d 100644 --- a/workspace.json +++ b/workspace.json @@ -16,6 +16,7 @@ "@herb-tools/rewriter": "javascript/packages/rewriter", "@herb-tools/tailwind-class-sorter": "javascript/packages/tailwind-class-sorter", "stimulus-lint": "javascript/packages/stimulus-lint", + "turbo-lint": "javascript/packages/turbo-lint", "herb-language-server": "javascript/packages/herb-language-server", "vscode": "javascript/packages/vscode", "docs": "./docs", From 80061de09d5066bb529086c2ae86b8a741aabb9a Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 15:33:02 +0100 Subject: [PATCH 07/17] Implement boilerplate --- javascript/packages/turbo-lint/bin/turbo-lint | 3 + javascript/packages/turbo-lint/src/cli.ts | 38 +++ .../packages/turbo-lint/src/default-rules.ts | 10 + javascript/packages/turbo-lint/src/index.ts | 5 + javascript/packages/turbo-lint/src/linter.ts | 61 ++++ .../src/rules/html-turbo-permanent.ts | 47 +++ .../packages/turbo-lint/src/rules/index.ts | 1 + .../turbo-lint/src/rules/rule-utils.ts | 161 +++++++++++ .../packages/turbo-lint/src/turbo-lint.ts | 6 + javascript/packages/turbo-lint/src/types.ts | 32 +++ .../packages/turbo-lint/test/basic.test.ts | 46 +++ .../test/helpers/linter-test-helper.ts | 271 ++++++++++++++++++ .../test/rules/html-turbo-permanent.test.ts | 24 ++ javascript/packages/turbo-lint/test/test.html | 1 + .../packages/turbo-lint/test/valid.html | 1 + 15 files changed, 707 insertions(+) create mode 100755 javascript/packages/turbo-lint/bin/turbo-lint create mode 100644 javascript/packages/turbo-lint/src/cli.ts create mode 100644 javascript/packages/turbo-lint/src/default-rules.ts create mode 100644 javascript/packages/turbo-lint/src/index.ts create mode 100644 javascript/packages/turbo-lint/src/linter.ts create mode 100644 javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts create mode 100644 javascript/packages/turbo-lint/src/rules/index.ts create mode 100644 javascript/packages/turbo-lint/src/rules/rule-utils.ts create mode 100644 javascript/packages/turbo-lint/src/turbo-lint.ts create mode 100644 javascript/packages/turbo-lint/src/types.ts create mode 100644 javascript/packages/turbo-lint/test/basic.test.ts create mode 100644 javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts create mode 100644 javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts create mode 100644 javascript/packages/turbo-lint/test/test.html create mode 100644 javascript/packages/turbo-lint/test/valid.html diff --git a/javascript/packages/turbo-lint/bin/turbo-lint b/javascript/packages/turbo-lint/bin/turbo-lint new file mode 100755 index 000000000..631e19e43 --- /dev/null +++ b/javascript/packages/turbo-lint/bin/turbo-lint @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import("../dist/turbo-lint.js") diff --git a/javascript/packages/turbo-lint/src/cli.ts b/javascript/packages/turbo-lint/src/cli.ts new file mode 100644 index 000000000..d072c6287 --- /dev/null +++ b/javascript/packages/turbo-lint/src/cli.ts @@ -0,0 +1,38 @@ +import { Herb } from "@herb-tools/node-wasm" +import { TurboLinter } from "./linter.js" +import { defaultRules } from "./default-rules.js" + +export class CLI { + async run() { + console.log("Turbo Lint CLI") + + const args = process.argv.slice(2) + + if (args.length === 0) { + console.log("Usage: turbo-lint ") + process.exit(1) + } + + const filePath = args[0] + + // Load the Herb backend + await Herb.load() + + const linter = new TurboLinter(Herb, defaultRules) + + try { + const result = await linter.lintFile(filePath) + + console.log(`\nFound ${result.errors} errors and ${result.warnings} warnings\n`) + + for (const offense of result.offenses) { + console.log(`${filePath} - ${offense.severity}: ${offense.message} (${offense.rule})`) + } + + process.exit(result.errors > 0 ? 1 : 0) + } catch (error) { + console.error("Error:", error) + process.exit(1) + } + } +} diff --git a/javascript/packages/turbo-lint/src/default-rules.ts b/javascript/packages/turbo-lint/src/default-rules.ts new file mode 100644 index 000000000..45f2f0b23 --- /dev/null +++ b/javascript/packages/turbo-lint/src/default-rules.ts @@ -0,0 +1,10 @@ +import { HtmlTurboPermanentRule } from "./rules/html-turbo-permanent.js" + +import type { RuleClass } from "./types.js" + +/** + * Default set of Turbo linting rules + */ +export const defaultRules: RuleClass[] = [ + HtmlTurboPermanentRule +] \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/index.ts b/javascript/packages/turbo-lint/src/index.ts new file mode 100644 index 000000000..8fcaae0ec --- /dev/null +++ b/javascript/packages/turbo-lint/src/index.ts @@ -0,0 +1,5 @@ +export { TurboLinter } from "./linter.js" +export { defaultRules } from "./default-rules.js" + +export * from "./types.js" +export * from "./rules/index.js" \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/linter.ts b/javascript/packages/turbo-lint/src/linter.ts new file mode 100644 index 000000000..1ef5a5eeb --- /dev/null +++ b/javascript/packages/turbo-lint/src/linter.ts @@ -0,0 +1,61 @@ +import { defaultRules } from "./default-rules.js" + +import type { RuleClass, LintResult, LintContext, LintOffense } from "./types.js" +import type { HerbBackend, ParseResult } from "@herb-tools/core" + +export class TurboLinter { + private herb: HerbBackend + private rules: RuleClass[] + + /** + * Creates a new TurboLinter instance. + * @param herb - The Herb backend instance for parsing HTML + * @param rules - Array of rule classes to use + */ + constructor(herb: HerbBackend, rules: RuleClass[] = defaultRules) { + this.herb = herb + this.rules = rules + } + + /** + * Lint source code with Turbo-specific context + * @param source - The source code to lint (HTML template) + * @param context - Optional context for linting + */ + lint(source: string, context?: Partial): LintResult { + const parseResult = this.herb.parse(source) as ParseResult + + const allOffenses: LintOffense[] = [] + + // Instantiate and run each rule + for (const RuleConstructor of this.rules) { + const rule = new RuleConstructor() + const offenses = rule.check(parseResult, context) + allOffenses.push(...offenses) + } + + // Count offense severities + const errors = allOffenses.filter(o => o.severity === "error").length + const warnings = allOffenses.filter(o => o.severity === "warning").length + + return { + offenses: allOffenses, + errors, + warnings + } + } + + /** + * Lint an HTML file + */ + async lintFile(filePath: string): Promise { + const fs = await import("fs") + const source = fs.readFileSync(filePath, "utf-8") + + const context: Partial = { + fileName: filePath + } + + return this.lint(source, context) + } +} diff --git a/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts b/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts new file mode 100644 index 000000000..e4e1bf248 --- /dev/null +++ b/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts @@ -0,0 +1,47 @@ +import { getTagName, hasAttribute, getAttributeValue, findAttributeByName, getAttributes } from "./rule-utils.js" +import { ParserRule } from "../types.js" +import { Visitor } from "@herb-tools/core" + +import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" +import type { LintContext, LintOffense } from "../types.js" + +class HTMLTurboPermanentVisitor extends Visitor { + public readonly offenses: LintOffense[] = [] + private ruleName: string + + constructor(ruleName: string) { + super() + this.ruleName = ruleName + } + + visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { + this.checkTurboPermanentAttribute(node) + super.visitHTMLOpenTagNode(node) + } + + private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { + const attribute = findAttributeByName(getAttributes(node), "data-turbo-permanent") + if (!attribute) return + + if (getAttributeValue(attribute) === "false") { + this.offenses.push({ + rule: this.ruleName, + code: this.ruleName, + source: "Turbo Linter", + message: 'Attribute `data-turbo-permanent` should not contain value "false"', + location: node.tag_name!.location, + severity: "error" + }) + } + } +} + +export class HtmlTurboPermanentRule extends ParserRule { + name = "html-turbo-permanent" + + check(result: ParseResult, context?: Partial): LintOffense[] { + const visitor = new HTMLTurboPermanentVisitor(this.name) + visitor.visit(result.value) + return visitor.offenses + } +} \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/rules/index.ts b/javascript/packages/turbo-lint/src/rules/index.ts new file mode 100644 index 000000000..653553a1a --- /dev/null +++ b/javascript/packages/turbo-lint/src/rules/index.ts @@ -0,0 +1 @@ +export { HtmlTurboPermanentRule } from "./html-turbo-permanent.js" \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/rules/rule-utils.ts b/javascript/packages/turbo-lint/src/rules/rule-utils.ts new file mode 100644 index 000000000..689defc17 --- /dev/null +++ b/javascript/packages/turbo-lint/src/rules/rule-utils.ts @@ -0,0 +1,161 @@ +import { + getStaticAttributeName, + hasDynamicAttributeName as hasNodeDynamicAttributeName, + getCombinedAttributeName, + hasERBOutput, + getStaticContentFromNodes, + hasStaticContent, + isEffectivelyStatic, + getValidatableStaticContent +} from "@herb-tools/core" + +import type { + ERBContentNode, + HTMLAttributeNameNode, + HTMLAttributeNode, + HTMLAttributeValueNode, + HTMLElementNode, + HTMLOpenTagNode, + LiteralNode, + LexResult, + Token, + Node +} from "@herb-tools/core" + +import type * as Nodes from "@herb-tools/core" + +/** + * Gets attributes from an HTMLOpenTagNode + */ +export function getAttributes(node: HTMLOpenTagNode): HTMLAttributeNode[] { + return node.children.filter(node => node.type === "AST_HTML_ATTRIBUTE_NODE") as HTMLAttributeNode[] +} + +/** + * Gets the tag name from an HTML tag node (lowercased) + */ +export function getTagName(node: HTMLElementNode | HTMLOpenTagNode | null | undefined): string | null { + if (!node) return null + + return node.tag_name?.value.toLowerCase() || null +} + +/** + * Gets the attribute name from an HTMLAttributeNode (lowercased) + * Returns null if the attribute name contains dynamic content (ERB) + */ +export function getAttributeName(attributeNode: HTMLAttributeNode, lowercase = true): string | null { + if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { + const nameNode = attributeNode.name as HTMLAttributeNameNode + const staticName = getStaticAttributeName(nameNode) + + if (!lowercase) return staticName + + return staticName ? staticName.toLowerCase() : null + } + + return null +} + +/** + * Checks if an attribute has a dynamic (ERB-containing) name + */ +export function hasDynamicAttributeName(attributeNode: HTMLAttributeNode): boolean { + if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { + const nameNode = attributeNode.name as HTMLAttributeNameNode + return hasNodeDynamicAttributeName(nameNode) + } + + return false +} + +/** + * Gets the combined string representation of an attribute name (for debugging) + * This includes both static content and ERB syntax + */ +export function getCombinedAttributeNameString(attributeNode: HTMLAttributeNode): string { + if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { + const nameNode = attributeNode.name as HTMLAttributeNameNode + + return getCombinedAttributeName(nameNode) + } + + return "" +} + +/** + * Gets the attribute value content from an HTMLAttributeValueNode + */ +export function getAttributeValue(attributeNode: HTMLAttributeNode): string | null { + const valueNode: HTMLAttributeValueNode | null = attributeNode.value as HTMLAttributeValueNode + + if (valueNode === null) return null + + if (valueNode.type !== "AST_HTML_ATTRIBUTE_VALUE_NODE" || !valueNode.children?.length) { + return null + } + + let result = "" + + for (const child of valueNode.children) { + switch (child.type) { + case "AST_ERB_CONTENT_NODE": { + const erbNode = child as ERBContentNode + + if (erbNode.content) { + result += `${erbNode.tag_opening?.value}${erbNode.content.value}${erbNode.tag_closing?.value}` + } + + break + } + + case "AST_LITERAL_NODE": { + result += (child as LiteralNode).content + break + } + } + } + + return result +} + +/** + * Checks if an attribute has a value + */ +export function hasAttributeValue(attributeNode: HTMLAttributeNode): boolean { + return attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" +} + +/** + * Finds an attribute by name in a list of attributes + */ +export function findAttributeByName(attributes: Node[], attributeName: string): HTMLAttributeNode | null { + for (const child of attributes) { + if (child.type === "AST_HTML_ATTRIBUTE_NODE") { + const attributeNode = child as HTMLAttributeNode + const name = getAttributeName(attributeNode) + + if (name === attributeName.toLowerCase()) { + return attributeNode + } + } + } + + return null +} + +/** + * Checks if a tag has a specific attribute + */ +export function hasAttribute(node: HTMLOpenTagNode, attributeName: string): boolean { + return getAttribute(node, attributeName) !== null +} + +/** + * Checks if a tag has a specific attribute + */ +export function getAttribute(node: HTMLOpenTagNode, attributeName: string): HTMLAttributeNode | null { + const attributes = getAttributes(node) + + return findAttributeByName(attributes, attributeName) +} \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/turbo-lint.ts b/javascript/packages/turbo-lint/src/turbo-lint.ts new file mode 100644 index 000000000..dd8f1b5c3 --- /dev/null +++ b/javascript/packages/turbo-lint/src/turbo-lint.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import { CLI } from "./cli.js" + +const cli = new CLI() +cli.run() diff --git a/javascript/packages/turbo-lint/src/types.ts b/javascript/packages/turbo-lint/src/types.ts new file mode 100644 index 000000000..9bc214666 --- /dev/null +++ b/javascript/packages/turbo-lint/src/types.ts @@ -0,0 +1,32 @@ +import type { Herb } from "@herb-tools/node-wasm" +import type { ParseResult, Location, Diagnostic } from "@herb-tools/core" + +export type HerbType = typeof Herb + +export type LintSeverity = "error" | "warning" | "info" | "hint" + +export interface LintContext { + fileName?: string +} + +export interface LintOffense extends Diagnostic { + rule: string + severity: LintSeverity +} + +export interface LintResult { + offenses: LintOffense[] + errors: number + warnings: number +} + +export interface LinterConfig { + rules?: Record +} + +export abstract class ParserRule { + abstract name: string + abstract check(result: ParseResult, context?: Partial): LintOffense[] +} + +export type RuleClass = new () => ParserRule \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/basic.test.ts b/javascript/packages/turbo-lint/test/basic.test.ts new file mode 100644 index 000000000..d944e44a4 --- /dev/null +++ b/javascript/packages/turbo-lint/test/basic.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, beforeAll } from "vitest" +import { Herb } from "@herb-tools/node-wasm" +import { TurboLinter, defaultRules } from "../src/index.js" + +describe("TurboLinter basic test", () => { + beforeAll(async () => { + await Herb.load() + }) + + it("should create linter instance", () => { + const linter = new TurboLinter(Herb, defaultRules) + expect(linter).toBeDefined() + expect(linter.getRuleCount()).toBeGreaterThan(0) + }) + + it("should lint turbo templates", () => { + const linter = new TurboLinter(Herb, defaultRules) + + const source = ` +
+ 1 item +
+ ` + + const result = linter.lint(source) + expect(result).toBeDefined() + expect(result.offenses).toBeDefined() + expect(result.offenses).toHaveLength(0) + }) + + it("should detect turbo-permanent issues", () => { + const linter = new TurboLinter(Herb, defaultRules) + + const source = ` +
+ 1 item +
+ ` + + const result = linter.lint(source) + expect(result).toBeDefined() + expect(result.offenses).toBeDefined() + expect(result.offenses).toHaveLength(1) + expect(result.offenses[0].rule).toBe("html-turbo-permanent") + }) +}) \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts b/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts new file mode 100644 index 000000000..3f2bac811 --- /dev/null +++ b/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts @@ -0,0 +1,271 @@ +import { beforeAll, afterEach, expect } from "vitest" + +import { Herb } from "@herb-tools/node-wasm" +import { TurboLinter } from "../../src/linter.js" + +import type { RuleClass } from "../../src/types.js" + +interface ExpectedLocation { + line?: number + column?: number +} + +type LocationInput = ExpectedLocation | [number, number] | [number] + +interface ExpectedOffense { + message: string + location?: ExpectedLocation +} + +interface TestOptions { + context?: any + allowInvalidSyntax?: boolean +} + +interface LinterTestHelpers { + expectNoOffenses: (html: string, options?: any | TestOptions) => void + expectWarning: (message: string, location?: LocationInput) => void + expectError: (message: string, location?: LocationInput) => void + assertOffenses: (html: string, options?: any | TestOptions) => void +} + +/** + * Creates a test helper for linter rules that reduces boilerplate in tests. + */ +export function createLinterTest(rules: RuleClass | RuleClass[]): LinterTestHelpers { + const expectedWarnings: ExpectedOffense[] = [] + const expectedErrors: ExpectedOffense[] = [] + let hasAsserted = false + + const ruleClasses = Array.isArray(rules) ? rules : [rules] + const primaryRuleClass = ruleClasses[0] + const ruleInstance = new primaryRuleClass() + const isParserNoErrorsRule = ruleInstance.name === "parser-no-errors" + + beforeAll(async () => { + await Herb.load() + }) + + afterEach(() => { + if (!hasAsserted && (expectedWarnings.length > 0 || expectedErrors.length > 0)) { + const pendingCount = expectedWarnings.length + expectedErrors.length + + throw new Error( + `Test has ${pendingCount} pending expectation(s) that were never asserted. ` + + `Did you forget to call assertOffenses() or expectNoOffenses()?` + ) + } + + expectedWarnings.length = 0 + expectedErrors.length = 0 + hasAsserted = false + }) + + const expectNoOffenses = (html: string, options?: any | TestOptions) => { + if (expectedWarnings.length > 0 || expectedErrors.length > 0) { + throw new Error( + "Cannot call expectNoOffenses() after registering expectations with expectWarning() or expectError()" + ) + } + + hasAsserted = true + + const context = options?.context ?? options + const allowInvalidSyntax = options?.allowInvalidSyntax ?? false + + if (!isParserNoErrorsRule) { + const parseResult = Herb.parse(html, { track_whitespace: true }) + const parserErrors = parseResult.recursiveErrors() + + if (allowInvalidSyntax && parserErrors.length === 0) { + throw new Error( + `Test has 'allowInvalidSyntax: true' but the HTML is actually valid.\n` + + `Remove the 'allowInvalidSyntax' option since the HTML parses without errors.\n` + + `Source:\n${html}` + ) + } + + if (!allowInvalidSyntax && parserErrors.length > 0) { + const formattedErrors = parserErrors.map(error => ` - ${error.message} (${error.type}) at ${error.location.start.line}:${error.location.start.column}`).join('\n') + + throw new Error( + `Test HTML has parser errors. Fix the HTML before testing the linter rule.\n` + + `Source:\n${html}\n\n` + + `Parser errors:\n${formattedErrors}` + ) + } + } + + const linter = new TurboLinter(Herb, ruleClasses) + const lintResult = linter.lint(html, context) + + const ruleName = ruleInstance.name + const primaryOffenses = lintResult.offenses.filter(offense => offense.rule === ruleName) + + expect(primaryOffenses).toHaveLength(0) + } + + const normalizeLocation = (location?: LocationInput): ExpectedLocation | undefined => { + if (!location) return undefined + + if (Array.isArray(location)) { + return location.length === 2 + ? { line: location[0], column: location[1] } + : { line: location[0] } + } + return location + } + + const expectWarning = (message: string, location?: LocationInput) => { + expectedWarnings.push({ message, location: normalizeLocation(location) }) + } + + const expectError = (message: string, location?: LocationInput) => { + expectedErrors.push({ message, location: normalizeLocation(location) }) + } + + const assertOffenses = (html: string, options?: any | TestOptions) => { + if (expectedWarnings.length === 0 && expectedErrors.length === 0) { + throw new Error( + "Cannot call assertOffenses() with no expectations. Use expectNoOffenses() instead." + ) + } + + hasAsserted = true + + const context = options?.context ?? options + const allowInvalidSyntax = options?.allowInvalidSyntax ?? false + + if (!isParserNoErrorsRule) { + const parseResult = Herb.parse(html, { track_whitespace: true }) + const parserErrors = parseResult.recursiveErrors() + + if (allowInvalidSyntax && parserErrors.length === 0) { + throw new Error( + `Test has 'allowInvalidSyntax: true' but the HTML is actually valid.\n` + + `Remove the 'allowInvalidSyntax' option since the HTML parses without errors.\n` + + `Source:\n${html}` + ) + } + + if (!allowInvalidSyntax && parserErrors.length > 0) { + const formattedErrors = parserErrors.map(error => ` - ${error.message} (${error.type}) at ${error.location.start.line}:${error.location.start.column}`).join('\n') + + throw new Error( + `Test HTML has parser errors. Fix the HTML before testing the linter rule.\n` + + `Source:\n${html}\n\n` + + `Parser errors:\n${formattedErrors}` + ) + } + } + + const linter = new TurboLinter(Herb, ruleClasses) + const lintResult = linter.lint(html, context) + const ruleName = ruleInstance.name + + const primaryOffenses = lintResult.offenses.filter(o => o.rule === ruleName) + const primaryErrors = primaryOffenses.filter(o => o.severity === "error") + const primaryWarnings = primaryOffenses.filter(o => o.severity === "warning") + + if (primaryErrors.length !== expectedErrors.length) { + throw new Error( + `Expected ${expectedErrors.length} error(s) from rule "${ruleName}" but found ${primaryErrors.length}.\n` + + `Expected:\n${expectedErrors.map(e => ` - "${e.message}"`).join('\n')}\n` + + `Actual:\n${primaryErrors.map(o => ` - "${o.message}" at ${o.location.start.line}:${o.location.start.column}`).join('\n')}` + ) + } + + if (primaryWarnings.length !== expectedWarnings.length) { + throw new Error( + `Expected ${expectedWarnings.length} warning(s) from rule "${ruleName}" but found ${primaryWarnings.length}.\n` + + `Expected:\n${expectedWarnings.map(w => ` - "${w.message}"`).join('\n')}\n` + + `Actual:\n${primaryWarnings.map(o => ` - "${o.message}" at ${o.location.start.line}:${o.location.start.column}`).join('\n')}` + ) + } + + primaryOffenses.forEach(offense => { + expect(offense.rule).toBe(ruleName) + }) + + const actualErrors = primaryErrors + const actualWarnings = primaryWarnings + + matchOffenses(expectedErrors, actualErrors, "error") + matchOffenses(expectedWarnings, actualWarnings, "warning") + + expectedWarnings.length = 0 + expectedErrors.length = 0 + } + + return { + expectNoOffenses, + expectWarning, + expectError, + assertOffenses + } +} + +/** + * Matches expected offenses to actual offenses in an order-independent way + */ +function matchOffenses( + expected: ExpectedOffense[], + actual: any[], + severity: "error" | "warning" +) { + const unmatched = [...expected] + const unmatchedActual = [...actual] + + for (const actualOffense of actual) { + const matchIndex = unmatched.findIndex(exp => { + if (exp.message !== actualOffense.message) { + return false + } + + if (exp.location?.line !== undefined && exp.location.line !== actualOffense.location.start.line) { + return false + } + + if (exp.location?.column !== undefined && exp.location.column !== actualOffense.location.start.column) { + return false + } + + return true + }) + + if (matchIndex !== -1) { + unmatched.splice(matchIndex, 1) + const actualIndex = unmatchedActual.findIndex(o => o === actualOffense) + if (actualIndex !== -1) { + unmatchedActual.splice(actualIndex, 1) + } + } + } + + if (unmatched.length > 0 || unmatchedActual.length > 0) { + const errors: string[] = [] + + if (unmatched.length > 0) { + errors.push(`Expected ${severity}(s) not found:`) + unmatched.forEach(exp => { + const location = exp.location?.line !== undefined + ? exp.location?.column !== undefined + ? ` at ${exp.location.line}:${exp.location.column}` + : ` at line ${exp.location.line}` + : "" + errors.push(` - "${exp.message}"${location}`) + }) + } + + if (unmatchedActual.length > 0) { + errors.push(`Unexpected ${severity}(s) found:`) + unmatchedActual.forEach(offense => { + errors.push( + ` - "${offense.message}" at ${offense.location.start.line}:${offense.location.start.column}` + ) + }) + } + + throw new Error(errors.join("\n")) + } +} \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts b/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts new file mode 100644 index 000000000..261a5a22a --- /dev/null +++ b/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts @@ -0,0 +1,24 @@ +import { describe, test } from "vitest" +import { HtmlTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" +import { createLinterTest } from "../helpers/linter-test-helper.js" + +const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HtmlTurboPermanentRule) + +describe("html-turbo-permanent", () => { + test("passes when no explicit value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("passes when empty explicit value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("passes when string true value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("fails when passing permanent=false", () => { + expectError('Attribute `data-turbo-permanent` should not contain value "false"') + assertOffenses('
1 item
') + }) +}) \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/test.html b/javascript/packages/turbo-lint/test/test.html new file mode 100644 index 000000000..333892e42 --- /dev/null +++ b/javascript/packages/turbo-lint/test/test.html @@ -0,0 +1 @@ +
Test
diff --git a/javascript/packages/turbo-lint/test/valid.html b/javascript/packages/turbo-lint/test/valid.html new file mode 100644 index 000000000..8e30faca1 --- /dev/null +++ b/javascript/packages/turbo-lint/test/valid.html @@ -0,0 +1 @@ +
Valid
From 99fb43686ac50feaf058f68e0668f4b28dbd59cd Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 15:41:00 +0100 Subject: [PATCH 08/17] Update rule --- .../src/rules/html-turbo-permanent.ts | 6 +++--- .../packages/turbo-lint/test/basic.test.ts | 2 +- .../test/rules/html-turbo-permanent.test.ts | 18 ++++++++++-------- javascript/packages/turbo-lint/test/test.html | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts b/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts index e4e1bf248..9944e8558 100644 --- a/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts +++ b/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts @@ -23,12 +23,12 @@ class HTMLTurboPermanentVisitor extends Visitor { const attribute = findAttributeByName(getAttributes(node), "data-turbo-permanent") if (!attribute) return - if (getAttributeValue(attribute) === "false") { + if (getAttributeValue(attribute) !== null) { this.offenses.push({ rule: this.ruleName, code: this.ruleName, source: "Turbo Linter", - message: 'Attribute `data-turbo-permanent` should not contain value "false"', + message: 'Attribute `data-turbo-permanent` should not contain any value', location: node.tag_name!.location, severity: "error" }) @@ -44,4 +44,4 @@ export class HtmlTurboPermanentRule extends ParserRule { visitor.visit(result.value) return visitor.offenses } -} \ No newline at end of file +} diff --git a/javascript/packages/turbo-lint/test/basic.test.ts b/javascript/packages/turbo-lint/test/basic.test.ts index d944e44a4..82ecc2a90 100644 --- a/javascript/packages/turbo-lint/test/basic.test.ts +++ b/javascript/packages/turbo-lint/test/basic.test.ts @@ -43,4 +43,4 @@ describe("TurboLinter basic test", () => { expect(result.offenses).toHaveLength(1) expect(result.offenses[0].rule).toBe("html-turbo-permanent") }) -}) \ No newline at end of file +}) diff --git a/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts b/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts index 261a5a22a..c0d7e306a 100644 --- a/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts +++ b/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts @@ -9,16 +9,18 @@ describe("html-turbo-permanent", () => { expectNoOffenses('
1 item
') }) - test("passes when empty explicit value is given", () => { - expectNoOffenses('
1 item
') - }) - - test("passes when string true value is given", () => { - expectNoOffenses('
1 item
') + test("fails when string true value is given", () => { + expectError('Attribute `data-turbo-permanent` should not contain any value') + assertOffenses('
1 item
') }) test("fails when passing permanent=false", () => { - expectError('Attribute `data-turbo-permanent` should not contain value "false"') + expectError('Attribute `data-turbo-permanent` should not contain any value') assertOffenses('
1 item
') }) -}) \ No newline at end of file + + test("fails when other value is passed", () => { + expectError('Attribute `data-turbo-permanent` should not contain any value') + assertOffenses('
1 item
') + }) +}) diff --git a/javascript/packages/turbo-lint/test/test.html b/javascript/packages/turbo-lint/test/test.html index 333892e42..f0cf79b7e 100644 --- a/javascript/packages/turbo-lint/test/test.html +++ b/javascript/packages/turbo-lint/test/test.html @@ -1 +1 @@ -
Test
+
Test
From 7ace6fbc160fd544ff19a2797e2827d84904dc7e Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 15:47:02 +0100 Subject: [PATCH 09/17] Remove duplicates --- .../linter/docs/rules/html-turbo-permanent.md | 32 ----------------- .../linter/src/rules/html-turbo-permanent.ts | 35 ------------------- javascript/packages/linter/src/rules/index.ts | 1 - .../test/rules/html-turbo-permanent.test.ts | 25 ------------- 4 files changed, 93 deletions(-) delete mode 100644 javascript/packages/linter/docs/rules/html-turbo-permanent.md delete mode 100644 javascript/packages/linter/src/rules/html-turbo-permanent.ts delete mode 100644 javascript/packages/linter/test/rules/html-turbo-permanent.test.ts diff --git a/javascript/packages/linter/docs/rules/html-turbo-permanent.md b/javascript/packages/linter/docs/rules/html-turbo-permanent.md deleted file mode 100644 index 09e8832a7..000000000 --- a/javascript/packages/linter/docs/rules/html-turbo-permanent.md +++ /dev/null @@ -1,32 +0,0 @@ -# Linter Rule: HTML Turbo permanent attribute usage - -**Rule:** `html-turbo-permanent` - -## Description - -Ensure that turbo permanent attributes are used correctly in HTML elements. The `data-turbo-permanent` attribute is used to mark elements that should persist across page navigations in Turbo Drive. - -## Rationale - -Data turbo permanent is active if the attribute is present, `data-turbo-permanent="false"` behaves the same as `data-turbo-permanent`. This should be disallowed to avoid confusion. - -## Examples - -### ✅ Good - -```html -
1 item
- -
1 item
-``` - -### 🚫 Bad - -```html -
1 item
-``` - -## References - -* [Turbo: `data-turbo-permanent` attribute](https://turbo.hotwired.dev/handbook/drive#permanent-elements) -* [HTML: Boolean Attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) diff --git a/javascript/packages/linter/src/rules/html-turbo-permanent.ts b/javascript/packages/linter/src/rules/html-turbo-permanent.ts deleted file mode 100644 index d541d2243..000000000 --- a/javascript/packages/linter/src/rules/html-turbo-permanent.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BaseRuleVisitor, getTagName, hasAttribute, getAttributeValue, findAttributeByName, getAttributes } from "./rule-utils.js" - -import { ParserRule } from "../types.js" -import type { LintOffense, LintContext } from "../types.js" -import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" - -class HTMLTurboPermanentVisitor extends BaseRuleVisitor { - visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { - this.checkTurboPermanentAttribute(node) - super.visitHTMLOpenTagNode(node) - } - - private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { - const attribute = findAttributeByName(getAttributes(node), "data-turbo-permanent") - if (!attribute) return - - if (getAttributeValue(attribute) === "true") { - this.addOffense( - 'Attribute `data-turbo-permanent` should not contain value "false"', - node.tag_name!.location, - "error" - ) - } - } -} - -export class HTMLTurboPermanentRule extends ParserRule { - name = "html-turbo-permanent" - - check(result: ParseResult, context?: Partial): LintOffense[] { - const visitor = new HTMLTurboPermanentVisitor(this.name, context) - visitor.visit(result.value) - return visitor.offenses - } -} diff --git a/javascript/packages/linter/src/rules/index.ts b/javascript/packages/linter/src/rules/index.ts index 42921a9cb..815093506 100644 --- a/javascript/packages/linter/src/rules/index.ts +++ b/javascript/packages/linter/src/rules/index.ts @@ -59,7 +59,6 @@ export * from "./herb-disable-comment-valid-rule-name.js" export * from "./html-allowed-script-type.js" export * from "./html-anchor-require-href.js" -export * from "./html-turbo-permanent.js" export * from "./html-aria-label-is-well-formatted.js" export * from "./html-aria-level-must-be-valid.js" export * from "./html-aria-role-heading-requires-level.js" diff --git a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts deleted file mode 100644 index 935d8a13d..000000000 --- a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { describe, test } from "vitest" -import { HTMLTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" -import { createLinterTest } from "../helpers/linter-test-helper.js" - -const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HTMLTurboPermanentRule) - -describe("html-tubro-permanent", () => { - test("passes when no explicit value is given", () => { - expectNoOffenses('
1 item
') - }) - - test("passes when empty explicit value is given", () => { - expectError('') - assertOffenses('
1 item
') - }) - - test("passes when string true value is given", () => { - expectNoOffenses('
1 item
') - }) - - test("fails when passing permanent=false", () => { - expectError('Attribute `data-turbo-permanent` should not contain value "false"') - assertOffenses('
1 item
') - }) -}) From 0cd3beed212d97f0e52a4630ffea95211f72dd0a Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 16:07:07 +0100 Subject: [PATCH 10/17] Update link --- javascript/packages/turbo-lint/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/packages/turbo-lint/README.md b/javascript/packages/turbo-lint/README.md index 5babef305..b120bddbb 100644 --- a/javascript/packages/turbo-lint/README.md +++ b/javascript/packages/turbo-lint/README.md @@ -36,4 +36,4 @@ npx turbo-lint ## Rules -Rules are documented [here](./docs/rules/) +Rules are documented [here](./docs/rules/README.md) From a914c42e97aecb30e127bf43796f125aac128699 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 16:20:59 +0100 Subject: [PATCH 11/17] Update docs --- .../packages/turbo-lint/docs/rules/html-turbo-permanent.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md b/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md index 09e8832a7..0cebda8a8 100644 --- a/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md +++ b/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md @@ -16,14 +16,14 @@ Data turbo permanent is active if the attribute is present, `data-turbo-permanen ```html
1 item
- -
1 item
``` ### 🚫 Bad ```html +
1 item
1 item
+
1 item
``` ## References From 95314ffcfdf2c14a911231785062928f13239f31 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Wed, 5 Nov 2025 17:42:30 +0100 Subject: [PATCH 12/17] Fix CI --- javascript/packages/turbo-lint/src/linter.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/javascript/packages/turbo-lint/src/linter.ts b/javascript/packages/turbo-lint/src/linter.ts index 1ef5a5eeb..8640b0b5b 100644 --- a/javascript/packages/turbo-lint/src/linter.ts +++ b/javascript/packages/turbo-lint/src/linter.ts @@ -45,6 +45,13 @@ export class TurboLinter { } } + /** + * Get the number of rules loaded in the linter + */ + getRuleCount(): number { + return this.rules.length + } + /** * Lint an HTML file */ From 97109bef62b2e72f3bd4eeb7b508c0cf25afdc37 Mon Sep 17 00:00:00 2001 From: Dani Bengl Date: Wed, 6 May 2026 16:30:35 +0200 Subject: [PATCH 13/17] Move away from turbo-lint package --- .github/labeler.yml | 5 - .../packages/linter/docs/rules/README.md | 1 + .../linter/docs/rules/html-turbo-permanent.md | 32 + javascript/packages/linter/src/rules.ts | 2 + .../linter/src/rules/html-turbo-permanent.ts | 45 + .../test/__snapshots__/cli.test.ts.snap | 6 +- .../test/rules/html-turbo-permanent.test.ts | 37 + javascript/packages/turbo-lint/README.md | 39 - javascript/packages/turbo-lint/bin/turbo-lint | 3 - .../packages/turbo-lint/docs/rules/README.md | 7 - .../docs/rules/html-turbo-permanent.md | 32 - javascript/packages/turbo-lint/package.json | 50 - javascript/packages/turbo-lint/project.json | 33 - .../packages/turbo-lint/rollup.config.mjs | 85 -- javascript/packages/turbo-lint/src/cli.ts | 38 - .../packages/turbo-lint/src/default-rules.ts | 10 - javascript/packages/turbo-lint/src/index.ts | 5 - javascript/packages/turbo-lint/src/linter.ts | 68 -- .../src/rules/html-turbo-permanent.ts | 47 - .../packages/turbo-lint/src/rules/index.ts | 1 - .../turbo-lint/src/rules/rule-utils.ts | 161 --- .../packages/turbo-lint/src/turbo-lint.ts | 6 - javascript/packages/turbo-lint/src/types.ts | 32 - .../packages/turbo-lint/test/basic.test.ts | 46 - .../test/helpers/linter-test-helper.ts | 271 ----- .../test/rules/html-turbo-permanent.test.ts | 26 - javascript/packages/turbo-lint/test/test.html | 1 - .../packages/turbo-lint/test/valid.html | 1 - javascript/packages/turbo-lint/tsconfig.json | 20 - src/ast_nodes.c | 1010 +++++++++++++++++ src/ast_pretty_print.c | 657 +++++++++++ src/include/ast_nodes.h | 346 ++++++ src/include/ast_pretty_print.h | 17 + workspace.json | 1 - 34 files changed, 2150 insertions(+), 991 deletions(-) create mode 100644 javascript/packages/linter/docs/rules/html-turbo-permanent.md create mode 100644 javascript/packages/linter/src/rules/html-turbo-permanent.ts create mode 100644 javascript/packages/linter/test/rules/html-turbo-permanent.test.ts delete mode 100644 javascript/packages/turbo-lint/README.md delete mode 100755 javascript/packages/turbo-lint/bin/turbo-lint delete mode 100644 javascript/packages/turbo-lint/docs/rules/README.md delete mode 100644 javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md delete mode 100644 javascript/packages/turbo-lint/package.json delete mode 100644 javascript/packages/turbo-lint/project.json delete mode 100644 javascript/packages/turbo-lint/rollup.config.mjs delete mode 100644 javascript/packages/turbo-lint/src/cli.ts delete mode 100644 javascript/packages/turbo-lint/src/default-rules.ts delete mode 100644 javascript/packages/turbo-lint/src/index.ts delete mode 100644 javascript/packages/turbo-lint/src/linter.ts delete mode 100644 javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts delete mode 100644 javascript/packages/turbo-lint/src/rules/index.ts delete mode 100644 javascript/packages/turbo-lint/src/rules/rule-utils.ts delete mode 100644 javascript/packages/turbo-lint/src/turbo-lint.ts delete mode 100644 javascript/packages/turbo-lint/src/types.ts delete mode 100644 javascript/packages/turbo-lint/test/basic.test.ts delete mode 100644 javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts delete mode 100644 javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts delete mode 100644 javascript/packages/turbo-lint/test/test.html delete mode 100644 javascript/packages/turbo-lint/test/valid.html delete mode 100644 javascript/packages/turbo-lint/tsconfig.json create mode 100644 src/ast_nodes.c create mode 100644 src/ast_pretty_print.c create mode 100644 src/include/ast_nodes.h create mode 100644 src/include/ast_pretty_print.h diff --git a/.github/labeler.yml b/.github/labeler.yml index 27b9e931e..c73114eae 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -173,11 +173,6 @@ node: - any-glob-to-any-file: - '**/javascript/packages/node/**/*' -turbo-lint: - - changed-files: - - any-glob-to-any-file: - - '**/javascript/packages/turbo-lint/**/*' - stimulus-lint: - changed-files: - any-glob-to-any-file: diff --git a/javascript/packages/linter/docs/rules/README.md b/javascript/packages/linter/docs/rules/README.md index a506bccaf..a044b45ea 100644 --- a/javascript/packages/linter/docs/rules/README.md +++ b/javascript/packages/linter/docs/rules/README.md @@ -126,6 +126,7 @@ This page contains documentation for all Herb Linter rules. #### Turbo +- [`html-turbo-permanent`](./html-turbo-permanent.md) - Disallow values on `data-turbo-permanent` - [`turbo-permanent-require-id`](./turbo-permanent-require-id.md) - Require `id` attribute on elements with `data-turbo-permanent` diff --git a/javascript/packages/linter/docs/rules/html-turbo-permanent.md b/javascript/packages/linter/docs/rules/html-turbo-permanent.md new file mode 100644 index 000000000..f7bf8942b --- /dev/null +++ b/javascript/packages/linter/docs/rules/html-turbo-permanent.md @@ -0,0 +1,32 @@ +# Linter Rule: HTML Turbo permanent attribute usage + +**Rule:** `html-turbo-permanent` + +## Description + +Ensure that `data-turbo-permanent` is used correctly on HTML elements. The `data-turbo-permanent` attribute marks elements that should persist across page navigations in Turbo Drive. + +## Rationale + +`data-turbo-permanent` is active whenever the attribute is present, so `data-turbo-permanent="false"` behaves the same as `data-turbo-permanent` or `data-turbo-permanent="true"`. Allowing values is misleading and should be disallowed to avoid confusion. + +## Examples + +### ✅ Good + +```html +
1 item
+``` + +### 🚫 Bad + +```html +
1 item
+
1 item
+
1 item
+``` + +## References + +- [Turbo: `data-turbo-permanent` attribute](https://turbo.hotwired.dev/handbook/drive#permanent-elements) +- [HTML: Boolean Attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) diff --git a/javascript/packages/linter/src/rules.ts b/javascript/packages/linter/src/rules.ts index 7a4f935b3..91adc8fcb 100644 --- a/javascript/packages/linter/src/rules.ts +++ b/javascript/packages/linter/src/rules.ts @@ -93,6 +93,7 @@ import { HTMLNoUnderscoresInAttributeNamesRule } from "./rules/html-no-underscor import { HTMLRequireClosingTagsRule } from "./rules/html-require-closing-tags.js" import { HTMLRequireScriptNonceRule } from "./rules/html-require-script-nonce.js" import { HTMLTagNameLowercaseRule } from "./rules/html-tag-name-lowercase.js" +import { HTMLTurboPermanentRule } from "./rules/html-turbo-permanent.js" import { ParserNoErrorsRule } from "./rules/parser-no-errors.js" @@ -196,6 +197,7 @@ export const rules: RuleClass[] = [ HTMLRequireClosingTagsRule, HTMLRequireScriptNonceRule, HTMLTagNameLowercaseRule, + HTMLTurboPermanentRule, ParserNoErrorsRule, diff --git a/javascript/packages/linter/src/rules/html-turbo-permanent.ts b/javascript/packages/linter/src/rules/html-turbo-permanent.ts new file mode 100644 index 000000000..473204b06 --- /dev/null +++ b/javascript/packages/linter/src/rules/html-turbo-permanent.ts @@ -0,0 +1,45 @@ +import { BaseRuleVisitor } from "./rule-utils.js" +import { getAttribute, hasAttributeValue } from "@herb-tools/core" + +import { ParserRule } from "../types.js" +import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js" +import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" + +class HTMLTurboPermanentVisitor extends BaseRuleVisitor { + visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { + this.checkTurboPermanentAttribute(node) + super.visitHTMLOpenTagNode(node) + } + + private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { + const attribute = getAttribute(node, "data-turbo-permanent") + + if (!attribute) return + if (!hasAttributeValue(attribute)) return + + this.addOffense( + "Attribute `data-turbo-permanent` should not contain any value. Its presence alone enables the behavior, so values like `\"true\"` or `\"false\"` are misleading.", + attribute.value!.location, + ) + } +} + +export class HTMLTurboPermanentRule extends ParserRule { + static ruleName = "html-turbo-permanent" + static introducedIn = this.version("0.9.0") + + get defaultConfig(): FullRuleConfig { + return { + enabled: true, + severity: "error" + } + } + + check(result: ParseResult, context?: Partial): UnboundLintOffense[] { + const visitor = new HTMLTurboPermanentVisitor(this.ruleName, context) + + visitor.visit(result.value) + + return visitor.offenses + } +} diff --git a/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap b/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap index 1b38e8234..31a0a562e 100644 --- a/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap +++ b/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap @@ -881,7 +881,7 @@ exports[`CLI Output Formatting > formats JSON output correctly for bad file 1`] "summary": { "filesChecked": 1, "filesWithOffenses": 1, - "ruleCount": 83, + "ruleCount": 84, "totalErrors": 2, "totalHints": 0, "totalIgnored": 0, @@ -902,7 +902,7 @@ exports[`CLI Output Formatting > formats JSON output correctly for clean file 1` "summary": { "filesChecked": 1, "filesWithOffenses": 0, - "ruleCount": 83, + "ruleCount": 84, "totalErrors": 0, "totalHints": 0, "totalIgnored": 0, @@ -975,7 +975,7 @@ exports[`CLI Output Formatting > formats JSON output correctly for file with err "summary": { "filesChecked": 1, "filesWithOffenses": 1, - "ruleCount": 83, + "ruleCount": 84, "totalErrors": 2, "totalHints": 0, "totalIgnored": 0, diff --git a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts new file mode 100644 index 000000000..91f96ba18 --- /dev/null +++ b/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts @@ -0,0 +1,37 @@ +import { describe, test } from "vitest" +import { HTMLTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" +import { createLinterTest } from "../helpers/linter-test-helper.js" + +const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HTMLTurboPermanentRule) + +const MESSAGE = "Attribute `data-turbo-permanent` should not contain any value. Its presence alone enables the behavior, so values like `\"true\"` or `\"false\"` are misleading." + +describe("html-turbo-permanent", () => { + test("passes when no explicit value is given", () => { + expectNoOffenses('
1 item
') + }) + + test("passes when attribute is absent", () => { + expectNoOffenses('
1 item
') + }) + + test("fails with value `true`", () => { + expectError(MESSAGE) + assertOffenses('
1 item
') + }) + + test("fails with value `false`", () => { + expectError(MESSAGE) + assertOffenses('
1 item
') + }) + + test("fails with arbitrary value", () => { + expectError(MESSAGE) + assertOffenses('
1 item
') + }) + + test("fails with empty string value", () => { + expectError(MESSAGE) + assertOffenses('
1 item
') + }) +}) diff --git a/javascript/packages/turbo-lint/README.md b/javascript/packages/turbo-lint/README.md deleted file mode 100644 index b120bddbb..000000000 --- a/javascript/packages/turbo-lint/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Turbo Lint - -**Package**: [`turbo-lint`](https://www.npmjs.com/package/turbo-lint) - ---- - -Linter and static code analyzer for [Turbo](https://turbo.hotwired.dev/), powered by [Herb](https://github.com/marcoroth/herb). - -### Installation - -:::code-group - -```shell [npm] -npm install turbo-lint -``` - -```shell [yarn] -yarn add turbo-lint -``` - -```shell [pnpm] -pnpm add turbo-lint -``` - -```shell [bun] -bun add turbo-lint -``` -::: - - -### Run - -```bash -npx turbo-lint -``` - -## Rules - -Rules are documented [here](./docs/rules/README.md) diff --git a/javascript/packages/turbo-lint/bin/turbo-lint b/javascript/packages/turbo-lint/bin/turbo-lint deleted file mode 100755 index 631e19e43..000000000 --- a/javascript/packages/turbo-lint/bin/turbo-lint +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node - -import("../dist/turbo-lint.js") diff --git a/javascript/packages/turbo-lint/docs/rules/README.md b/javascript/packages/turbo-lint/docs/rules/README.md deleted file mode 100644 index b28b55ce8..000000000 --- a/javascript/packages/turbo-lint/docs/rules/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Linter Rules - -This page contains documentation for all Turbo Linter rules. - -## Available Rules - -- [`turbo-permanent`](./html-turbo-permanent.md) - Prevents invalid usage of the boolean attribute diff --git a/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md b/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md deleted file mode 100644 index 0cebda8a8..000000000 --- a/javascript/packages/turbo-lint/docs/rules/html-turbo-permanent.md +++ /dev/null @@ -1,32 +0,0 @@ -# Linter Rule: HTML Turbo permanent attribute usage - -**Rule:** `html-turbo-permanent` - -## Description - -Ensure that turbo permanent attributes are used correctly in HTML elements. The `data-turbo-permanent` attribute is used to mark elements that should persist across page navigations in Turbo Drive. - -## Rationale - -Data turbo permanent is active if the attribute is present, `data-turbo-permanent="false"` behaves the same as `data-turbo-permanent`. This should be disallowed to avoid confusion. - -## Examples - -### ✅ Good - -```html -
1 item
-``` - -### 🚫 Bad - -```html -
1 item
-
1 item
-
1 item
-``` - -## References - -* [Turbo: `data-turbo-permanent` attribute](https://turbo.hotwired.dev/handbook/drive#permanent-elements) -* [HTML: Boolean Attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) diff --git a/javascript/packages/turbo-lint/package.json b/javascript/packages/turbo-lint/package.json deleted file mode 100644 index 76250a1fb..000000000 --- a/javascript/packages/turbo-lint/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "turbo-lint", - "version": "0.1.0", - "description": "Linting rules for Turbo HTML+ERB view templates.", - "license": "MIT", - "homepage": "https://herb-tools.dev", - "bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60turbo-lint%60:%20", - "repository": { - "type": "git", - "url": "https://github.com/marcoroth/herb.git", - "directory": "javascript/packages/turbo-lint" - }, - "main": "./dist/index.cjs", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "bin": { - "turbo-lint": "./bin/turbo-lint" - }, - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/types/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "default": "./dist/index.js" - } - }, - "scripts": { - "clean": "rimraf dist", - "build": "yarn clean && tsc -b && rollup -c", - "watch": "tsc -b -w", - "test": "vitest run", - "test:watch": "vitest --watch", - "prepublishOnly": "yarn clean && yarn build && yarn test" - }, - "dependencies": { - "@herb-tools/core": "0.7.5", - "@herb-tools/highlighter": "0.7.5", - "@herb-tools/linter": "0.7.5", - "@herb-tools/node-wasm": "0.7.5" - }, - "files": [ - "package.json", - "README.md", - "docs/", - "src/", - "bin/", - "dist/" - ] -} diff --git a/javascript/packages/turbo-lint/project.json b/javascript/packages/turbo-lint/project.json deleted file mode 100644 index 897c4e95c..000000000 --- a/javascript/packages/turbo-lint/project.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "turbo-lint", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "javascript/packages/turbo-lint/src", - "projectType": "library", - "targets": { - "build": { - "executor": "nx:run-script", - "options": { - "script": "build" - }, - "dependsOn": [ - "@herb-tools/core:build", - "@herb-tools/node-wasm:build", - "@herb-tools/linter:build", - "@herb-tools/highlighter:build" - ] - }, - "test": { - "executor": "nx:run-script", - "options": { - "script": "test" - } - }, - "clean": { - "executor": "nx:run-script", - "options": { - "script": "clean" - } - } - }, - "tags": [] -} diff --git a/javascript/packages/turbo-lint/rollup.config.mjs b/javascript/packages/turbo-lint/rollup.config.mjs deleted file mode 100644 index 32bbe5254..000000000 --- a/javascript/packages/turbo-lint/rollup.config.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import typescript from "@rollup/plugin-typescript" -import { nodeResolve } from "@rollup/plugin-node-resolve" -import json from "@rollup/plugin-json" -import commonjs from "@rollup/plugin-commonjs" - -// Bundle the CLI entry point into a single CommonJS file. -// Exclude Node built-in so they remain as externals. -const external = [ - "path", - "url", - "fs", - "module", -] - -function isExternal(id) { - return ( - external.includes(id) || - external.some((pkg) => id === pkg || id.startsWith(pkg + "/")) - ) -} - -export default [ - // CLI entry point (CommonJS) - { - input: "src/turbo-lint.ts", - output: { - file: "dist/turbo-lint.js", - format: "cjs", - sourcemap: true, - }, - external: isExternal, - plugins: [ - nodeResolve(), - commonjs(), - json(), - typescript({ - tsconfig: "./tsconfig.json", - rootDir: "src/", - module: "esnext", - }), - ], - }, - - // Library exports (ESM) - { - input: "src/index.ts", - output: { - file: "dist/index.js", - format: "esm", - sourcemap: true, - }, - external: ["@herb-tools/core", "@herb-tools/linter", "@herb-tools/node-wasm", "turbo-parser"], - plugins: [ - nodeResolve(), - json(), - typescript({ - tsconfig: "./tsconfig.json", - declaration: true, - declarationDir: "./dist/types", - rootDir: "src/", - }), - ], - }, - - // Library exports (CommonJS) - { - input: "src/index.ts", - external: ["@herb-tools/core", "@herb-tools/linter", "@herb-tools/node-wasm", "turbo-parser"], - output: { - file: "dist/index.cjs", - format: "cjs", - sourcemap: true, - }, - plugins: [ - nodeResolve(), - commonjs(), - json(), - typescript({ - tsconfig: "./tsconfig.json", - rootDir: "src/", - module: "esnext", - }), - ], - }, -] diff --git a/javascript/packages/turbo-lint/src/cli.ts b/javascript/packages/turbo-lint/src/cli.ts deleted file mode 100644 index d072c6287..000000000 --- a/javascript/packages/turbo-lint/src/cli.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Herb } from "@herb-tools/node-wasm" -import { TurboLinter } from "./linter.js" -import { defaultRules } from "./default-rules.js" - -export class CLI { - async run() { - console.log("Turbo Lint CLI") - - const args = process.argv.slice(2) - - if (args.length === 0) { - console.log("Usage: turbo-lint ") - process.exit(1) - } - - const filePath = args[0] - - // Load the Herb backend - await Herb.load() - - const linter = new TurboLinter(Herb, defaultRules) - - try { - const result = await linter.lintFile(filePath) - - console.log(`\nFound ${result.errors} errors and ${result.warnings} warnings\n`) - - for (const offense of result.offenses) { - console.log(`${filePath} - ${offense.severity}: ${offense.message} (${offense.rule})`) - } - - process.exit(result.errors > 0 ? 1 : 0) - } catch (error) { - console.error("Error:", error) - process.exit(1) - } - } -} diff --git a/javascript/packages/turbo-lint/src/default-rules.ts b/javascript/packages/turbo-lint/src/default-rules.ts deleted file mode 100644 index 45f2f0b23..000000000 --- a/javascript/packages/turbo-lint/src/default-rules.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { HtmlTurboPermanentRule } from "./rules/html-turbo-permanent.js" - -import type { RuleClass } from "./types.js" - -/** - * Default set of Turbo linting rules - */ -export const defaultRules: RuleClass[] = [ - HtmlTurboPermanentRule -] \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/index.ts b/javascript/packages/turbo-lint/src/index.ts deleted file mode 100644 index 8fcaae0ec..000000000 --- a/javascript/packages/turbo-lint/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { TurboLinter } from "./linter.js" -export { defaultRules } from "./default-rules.js" - -export * from "./types.js" -export * from "./rules/index.js" \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/linter.ts b/javascript/packages/turbo-lint/src/linter.ts deleted file mode 100644 index 8640b0b5b..000000000 --- a/javascript/packages/turbo-lint/src/linter.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { defaultRules } from "./default-rules.js" - -import type { RuleClass, LintResult, LintContext, LintOffense } from "./types.js" -import type { HerbBackend, ParseResult } from "@herb-tools/core" - -export class TurboLinter { - private herb: HerbBackend - private rules: RuleClass[] - - /** - * Creates a new TurboLinter instance. - * @param herb - The Herb backend instance for parsing HTML - * @param rules - Array of rule classes to use - */ - constructor(herb: HerbBackend, rules: RuleClass[] = defaultRules) { - this.herb = herb - this.rules = rules - } - - /** - * Lint source code with Turbo-specific context - * @param source - The source code to lint (HTML template) - * @param context - Optional context for linting - */ - lint(source: string, context?: Partial): LintResult { - const parseResult = this.herb.parse(source) as ParseResult - - const allOffenses: LintOffense[] = [] - - // Instantiate and run each rule - for (const RuleConstructor of this.rules) { - const rule = new RuleConstructor() - const offenses = rule.check(parseResult, context) - allOffenses.push(...offenses) - } - - // Count offense severities - const errors = allOffenses.filter(o => o.severity === "error").length - const warnings = allOffenses.filter(o => o.severity === "warning").length - - return { - offenses: allOffenses, - errors, - warnings - } - } - - /** - * Get the number of rules loaded in the linter - */ - getRuleCount(): number { - return this.rules.length - } - - /** - * Lint an HTML file - */ - async lintFile(filePath: string): Promise { - const fs = await import("fs") - const source = fs.readFileSync(filePath, "utf-8") - - const context: Partial = { - fileName: filePath - } - - return this.lint(source, context) - } -} diff --git a/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts b/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts deleted file mode 100644 index 9944e8558..000000000 --- a/javascript/packages/turbo-lint/src/rules/html-turbo-permanent.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { getTagName, hasAttribute, getAttributeValue, findAttributeByName, getAttributes } from "./rule-utils.js" -import { ParserRule } from "../types.js" -import { Visitor } from "@herb-tools/core" - -import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" -import type { LintContext, LintOffense } from "../types.js" - -class HTMLTurboPermanentVisitor extends Visitor { - public readonly offenses: LintOffense[] = [] - private ruleName: string - - constructor(ruleName: string) { - super() - this.ruleName = ruleName - } - - visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { - this.checkTurboPermanentAttribute(node) - super.visitHTMLOpenTagNode(node) - } - - private checkTurboPermanentAttribute(node: HTMLOpenTagNode): void { - const attribute = findAttributeByName(getAttributes(node), "data-turbo-permanent") - if (!attribute) return - - if (getAttributeValue(attribute) !== null) { - this.offenses.push({ - rule: this.ruleName, - code: this.ruleName, - source: "Turbo Linter", - message: 'Attribute `data-turbo-permanent` should not contain any value', - location: node.tag_name!.location, - severity: "error" - }) - } - } -} - -export class HtmlTurboPermanentRule extends ParserRule { - name = "html-turbo-permanent" - - check(result: ParseResult, context?: Partial): LintOffense[] { - const visitor = new HTMLTurboPermanentVisitor(this.name) - visitor.visit(result.value) - return visitor.offenses - } -} diff --git a/javascript/packages/turbo-lint/src/rules/index.ts b/javascript/packages/turbo-lint/src/rules/index.ts deleted file mode 100644 index 653553a1a..000000000 --- a/javascript/packages/turbo-lint/src/rules/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { HtmlTurboPermanentRule } from "./html-turbo-permanent.js" \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/rules/rule-utils.ts b/javascript/packages/turbo-lint/src/rules/rule-utils.ts deleted file mode 100644 index 689defc17..000000000 --- a/javascript/packages/turbo-lint/src/rules/rule-utils.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { - getStaticAttributeName, - hasDynamicAttributeName as hasNodeDynamicAttributeName, - getCombinedAttributeName, - hasERBOutput, - getStaticContentFromNodes, - hasStaticContent, - isEffectivelyStatic, - getValidatableStaticContent -} from "@herb-tools/core" - -import type { - ERBContentNode, - HTMLAttributeNameNode, - HTMLAttributeNode, - HTMLAttributeValueNode, - HTMLElementNode, - HTMLOpenTagNode, - LiteralNode, - LexResult, - Token, - Node -} from "@herb-tools/core" - -import type * as Nodes from "@herb-tools/core" - -/** - * Gets attributes from an HTMLOpenTagNode - */ -export function getAttributes(node: HTMLOpenTagNode): HTMLAttributeNode[] { - return node.children.filter(node => node.type === "AST_HTML_ATTRIBUTE_NODE") as HTMLAttributeNode[] -} - -/** - * Gets the tag name from an HTML tag node (lowercased) - */ -export function getTagName(node: HTMLElementNode | HTMLOpenTagNode | null | undefined): string | null { - if (!node) return null - - return node.tag_name?.value.toLowerCase() || null -} - -/** - * Gets the attribute name from an HTMLAttributeNode (lowercased) - * Returns null if the attribute name contains dynamic content (ERB) - */ -export function getAttributeName(attributeNode: HTMLAttributeNode, lowercase = true): string | null { - if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { - const nameNode = attributeNode.name as HTMLAttributeNameNode - const staticName = getStaticAttributeName(nameNode) - - if (!lowercase) return staticName - - return staticName ? staticName.toLowerCase() : null - } - - return null -} - -/** - * Checks if an attribute has a dynamic (ERB-containing) name - */ -export function hasDynamicAttributeName(attributeNode: HTMLAttributeNode): boolean { - if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { - const nameNode = attributeNode.name as HTMLAttributeNameNode - return hasNodeDynamicAttributeName(nameNode) - } - - return false -} - -/** - * Gets the combined string representation of an attribute name (for debugging) - * This includes both static content and ERB syntax - */ -export function getCombinedAttributeNameString(attributeNode: HTMLAttributeNode): string { - if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") { - const nameNode = attributeNode.name as HTMLAttributeNameNode - - return getCombinedAttributeName(nameNode) - } - - return "" -} - -/** - * Gets the attribute value content from an HTMLAttributeValueNode - */ -export function getAttributeValue(attributeNode: HTMLAttributeNode): string | null { - const valueNode: HTMLAttributeValueNode | null = attributeNode.value as HTMLAttributeValueNode - - if (valueNode === null) return null - - if (valueNode.type !== "AST_HTML_ATTRIBUTE_VALUE_NODE" || !valueNode.children?.length) { - return null - } - - let result = "" - - for (const child of valueNode.children) { - switch (child.type) { - case "AST_ERB_CONTENT_NODE": { - const erbNode = child as ERBContentNode - - if (erbNode.content) { - result += `${erbNode.tag_opening?.value}${erbNode.content.value}${erbNode.tag_closing?.value}` - } - - break - } - - case "AST_LITERAL_NODE": { - result += (child as LiteralNode).content - break - } - } - } - - return result -} - -/** - * Checks if an attribute has a value - */ -export function hasAttributeValue(attributeNode: HTMLAttributeNode): boolean { - return attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" -} - -/** - * Finds an attribute by name in a list of attributes - */ -export function findAttributeByName(attributes: Node[], attributeName: string): HTMLAttributeNode | null { - for (const child of attributes) { - if (child.type === "AST_HTML_ATTRIBUTE_NODE") { - const attributeNode = child as HTMLAttributeNode - const name = getAttributeName(attributeNode) - - if (name === attributeName.toLowerCase()) { - return attributeNode - } - } - } - - return null -} - -/** - * Checks if a tag has a specific attribute - */ -export function hasAttribute(node: HTMLOpenTagNode, attributeName: string): boolean { - return getAttribute(node, attributeName) !== null -} - -/** - * Checks if a tag has a specific attribute - */ -export function getAttribute(node: HTMLOpenTagNode, attributeName: string): HTMLAttributeNode | null { - const attributes = getAttributes(node) - - return findAttributeByName(attributes, attributeName) -} \ No newline at end of file diff --git a/javascript/packages/turbo-lint/src/turbo-lint.ts b/javascript/packages/turbo-lint/src/turbo-lint.ts deleted file mode 100644 index dd8f1b5c3..000000000 --- a/javascript/packages/turbo-lint/src/turbo-lint.ts +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env node - -import { CLI } from "./cli.js" - -const cli = new CLI() -cli.run() diff --git a/javascript/packages/turbo-lint/src/types.ts b/javascript/packages/turbo-lint/src/types.ts deleted file mode 100644 index 9bc214666..000000000 --- a/javascript/packages/turbo-lint/src/types.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { Herb } from "@herb-tools/node-wasm" -import type { ParseResult, Location, Diagnostic } from "@herb-tools/core" - -export type HerbType = typeof Herb - -export type LintSeverity = "error" | "warning" | "info" | "hint" - -export interface LintContext { - fileName?: string -} - -export interface LintOffense extends Diagnostic { - rule: string - severity: LintSeverity -} - -export interface LintResult { - offenses: LintOffense[] - errors: number - warnings: number -} - -export interface LinterConfig { - rules?: Record -} - -export abstract class ParserRule { - abstract name: string - abstract check(result: ParseResult, context?: Partial): LintOffense[] -} - -export type RuleClass = new () => ParserRule \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/basic.test.ts b/javascript/packages/turbo-lint/test/basic.test.ts deleted file mode 100644 index 82ecc2a90..000000000 --- a/javascript/packages/turbo-lint/test/basic.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest" -import { Herb } from "@herb-tools/node-wasm" -import { TurboLinter, defaultRules } from "../src/index.js" - -describe("TurboLinter basic test", () => { - beforeAll(async () => { - await Herb.load() - }) - - it("should create linter instance", () => { - const linter = new TurboLinter(Herb, defaultRules) - expect(linter).toBeDefined() - expect(linter.getRuleCount()).toBeGreaterThan(0) - }) - - it("should lint turbo templates", () => { - const linter = new TurboLinter(Herb, defaultRules) - - const source = ` -
- 1 item -
- ` - - const result = linter.lint(source) - expect(result).toBeDefined() - expect(result.offenses).toBeDefined() - expect(result.offenses).toHaveLength(0) - }) - - it("should detect turbo-permanent issues", () => { - const linter = new TurboLinter(Herb, defaultRules) - - const source = ` -
- 1 item -
- ` - - const result = linter.lint(source) - expect(result).toBeDefined() - expect(result.offenses).toBeDefined() - expect(result.offenses).toHaveLength(1) - expect(result.offenses[0].rule).toBe("html-turbo-permanent") - }) -}) diff --git a/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts b/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts deleted file mode 100644 index 3f2bac811..000000000 --- a/javascript/packages/turbo-lint/test/helpers/linter-test-helper.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { beforeAll, afterEach, expect } from "vitest" - -import { Herb } from "@herb-tools/node-wasm" -import { TurboLinter } from "../../src/linter.js" - -import type { RuleClass } from "../../src/types.js" - -interface ExpectedLocation { - line?: number - column?: number -} - -type LocationInput = ExpectedLocation | [number, number] | [number] - -interface ExpectedOffense { - message: string - location?: ExpectedLocation -} - -interface TestOptions { - context?: any - allowInvalidSyntax?: boolean -} - -interface LinterTestHelpers { - expectNoOffenses: (html: string, options?: any | TestOptions) => void - expectWarning: (message: string, location?: LocationInput) => void - expectError: (message: string, location?: LocationInput) => void - assertOffenses: (html: string, options?: any | TestOptions) => void -} - -/** - * Creates a test helper for linter rules that reduces boilerplate in tests. - */ -export function createLinterTest(rules: RuleClass | RuleClass[]): LinterTestHelpers { - const expectedWarnings: ExpectedOffense[] = [] - const expectedErrors: ExpectedOffense[] = [] - let hasAsserted = false - - const ruleClasses = Array.isArray(rules) ? rules : [rules] - const primaryRuleClass = ruleClasses[0] - const ruleInstance = new primaryRuleClass() - const isParserNoErrorsRule = ruleInstance.name === "parser-no-errors" - - beforeAll(async () => { - await Herb.load() - }) - - afterEach(() => { - if (!hasAsserted && (expectedWarnings.length > 0 || expectedErrors.length > 0)) { - const pendingCount = expectedWarnings.length + expectedErrors.length - - throw new Error( - `Test has ${pendingCount} pending expectation(s) that were never asserted. ` + - `Did you forget to call assertOffenses() or expectNoOffenses()?` - ) - } - - expectedWarnings.length = 0 - expectedErrors.length = 0 - hasAsserted = false - }) - - const expectNoOffenses = (html: string, options?: any | TestOptions) => { - if (expectedWarnings.length > 0 || expectedErrors.length > 0) { - throw new Error( - "Cannot call expectNoOffenses() after registering expectations with expectWarning() or expectError()" - ) - } - - hasAsserted = true - - const context = options?.context ?? options - const allowInvalidSyntax = options?.allowInvalidSyntax ?? false - - if (!isParserNoErrorsRule) { - const parseResult = Herb.parse(html, { track_whitespace: true }) - const parserErrors = parseResult.recursiveErrors() - - if (allowInvalidSyntax && parserErrors.length === 0) { - throw new Error( - `Test has 'allowInvalidSyntax: true' but the HTML is actually valid.\n` + - `Remove the 'allowInvalidSyntax' option since the HTML parses without errors.\n` + - `Source:\n${html}` - ) - } - - if (!allowInvalidSyntax && parserErrors.length > 0) { - const formattedErrors = parserErrors.map(error => ` - ${error.message} (${error.type}) at ${error.location.start.line}:${error.location.start.column}`).join('\n') - - throw new Error( - `Test HTML has parser errors. Fix the HTML before testing the linter rule.\n` + - `Source:\n${html}\n\n` + - `Parser errors:\n${formattedErrors}` - ) - } - } - - const linter = new TurboLinter(Herb, ruleClasses) - const lintResult = linter.lint(html, context) - - const ruleName = ruleInstance.name - const primaryOffenses = lintResult.offenses.filter(offense => offense.rule === ruleName) - - expect(primaryOffenses).toHaveLength(0) - } - - const normalizeLocation = (location?: LocationInput): ExpectedLocation | undefined => { - if (!location) return undefined - - if (Array.isArray(location)) { - return location.length === 2 - ? { line: location[0], column: location[1] } - : { line: location[0] } - } - return location - } - - const expectWarning = (message: string, location?: LocationInput) => { - expectedWarnings.push({ message, location: normalizeLocation(location) }) - } - - const expectError = (message: string, location?: LocationInput) => { - expectedErrors.push({ message, location: normalizeLocation(location) }) - } - - const assertOffenses = (html: string, options?: any | TestOptions) => { - if (expectedWarnings.length === 0 && expectedErrors.length === 0) { - throw new Error( - "Cannot call assertOffenses() with no expectations. Use expectNoOffenses() instead." - ) - } - - hasAsserted = true - - const context = options?.context ?? options - const allowInvalidSyntax = options?.allowInvalidSyntax ?? false - - if (!isParserNoErrorsRule) { - const parseResult = Herb.parse(html, { track_whitespace: true }) - const parserErrors = parseResult.recursiveErrors() - - if (allowInvalidSyntax && parserErrors.length === 0) { - throw new Error( - `Test has 'allowInvalidSyntax: true' but the HTML is actually valid.\n` + - `Remove the 'allowInvalidSyntax' option since the HTML parses without errors.\n` + - `Source:\n${html}` - ) - } - - if (!allowInvalidSyntax && parserErrors.length > 0) { - const formattedErrors = parserErrors.map(error => ` - ${error.message} (${error.type}) at ${error.location.start.line}:${error.location.start.column}`).join('\n') - - throw new Error( - `Test HTML has parser errors. Fix the HTML before testing the linter rule.\n` + - `Source:\n${html}\n\n` + - `Parser errors:\n${formattedErrors}` - ) - } - } - - const linter = new TurboLinter(Herb, ruleClasses) - const lintResult = linter.lint(html, context) - const ruleName = ruleInstance.name - - const primaryOffenses = lintResult.offenses.filter(o => o.rule === ruleName) - const primaryErrors = primaryOffenses.filter(o => o.severity === "error") - const primaryWarnings = primaryOffenses.filter(o => o.severity === "warning") - - if (primaryErrors.length !== expectedErrors.length) { - throw new Error( - `Expected ${expectedErrors.length} error(s) from rule "${ruleName}" but found ${primaryErrors.length}.\n` + - `Expected:\n${expectedErrors.map(e => ` - "${e.message}"`).join('\n')}\n` + - `Actual:\n${primaryErrors.map(o => ` - "${o.message}" at ${o.location.start.line}:${o.location.start.column}`).join('\n')}` - ) - } - - if (primaryWarnings.length !== expectedWarnings.length) { - throw new Error( - `Expected ${expectedWarnings.length} warning(s) from rule "${ruleName}" but found ${primaryWarnings.length}.\n` + - `Expected:\n${expectedWarnings.map(w => ` - "${w.message}"`).join('\n')}\n` + - `Actual:\n${primaryWarnings.map(o => ` - "${o.message}" at ${o.location.start.line}:${o.location.start.column}`).join('\n')}` - ) - } - - primaryOffenses.forEach(offense => { - expect(offense.rule).toBe(ruleName) - }) - - const actualErrors = primaryErrors - const actualWarnings = primaryWarnings - - matchOffenses(expectedErrors, actualErrors, "error") - matchOffenses(expectedWarnings, actualWarnings, "warning") - - expectedWarnings.length = 0 - expectedErrors.length = 0 - } - - return { - expectNoOffenses, - expectWarning, - expectError, - assertOffenses - } -} - -/** - * Matches expected offenses to actual offenses in an order-independent way - */ -function matchOffenses( - expected: ExpectedOffense[], - actual: any[], - severity: "error" | "warning" -) { - const unmatched = [...expected] - const unmatchedActual = [...actual] - - for (const actualOffense of actual) { - const matchIndex = unmatched.findIndex(exp => { - if (exp.message !== actualOffense.message) { - return false - } - - if (exp.location?.line !== undefined && exp.location.line !== actualOffense.location.start.line) { - return false - } - - if (exp.location?.column !== undefined && exp.location.column !== actualOffense.location.start.column) { - return false - } - - return true - }) - - if (matchIndex !== -1) { - unmatched.splice(matchIndex, 1) - const actualIndex = unmatchedActual.findIndex(o => o === actualOffense) - if (actualIndex !== -1) { - unmatchedActual.splice(actualIndex, 1) - } - } - } - - if (unmatched.length > 0 || unmatchedActual.length > 0) { - const errors: string[] = [] - - if (unmatched.length > 0) { - errors.push(`Expected ${severity}(s) not found:`) - unmatched.forEach(exp => { - const location = exp.location?.line !== undefined - ? exp.location?.column !== undefined - ? ` at ${exp.location.line}:${exp.location.column}` - : ` at line ${exp.location.line}` - : "" - errors.push(` - "${exp.message}"${location}`) - }) - } - - if (unmatchedActual.length > 0) { - errors.push(`Unexpected ${severity}(s) found:`) - unmatchedActual.forEach(offense => { - errors.push( - ` - "${offense.message}" at ${offense.location.start.line}:${offense.location.start.column}` - ) - }) - } - - throw new Error(errors.join("\n")) - } -} \ No newline at end of file diff --git a/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts b/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts deleted file mode 100644 index c0d7e306a..000000000 --- a/javascript/packages/turbo-lint/test/rules/html-turbo-permanent.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, test } from "vitest" -import { HtmlTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" -import { createLinterTest } from "../helpers/linter-test-helper.js" - -const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HtmlTurboPermanentRule) - -describe("html-turbo-permanent", () => { - test("passes when no explicit value is given", () => { - expectNoOffenses('
1 item
') - }) - - test("fails when string true value is given", () => { - expectError('Attribute `data-turbo-permanent` should not contain any value') - assertOffenses('
1 item
') - }) - - test("fails when passing permanent=false", () => { - expectError('Attribute `data-turbo-permanent` should not contain any value') - assertOffenses('
1 item
') - }) - - test("fails when other value is passed", () => { - expectError('Attribute `data-turbo-permanent` should not contain any value') - assertOffenses('
1 item
') - }) -}) diff --git a/javascript/packages/turbo-lint/test/test.html b/javascript/packages/turbo-lint/test/test.html deleted file mode 100644 index f0cf79b7e..000000000 --- a/javascript/packages/turbo-lint/test/test.html +++ /dev/null @@ -1 +0,0 @@ -
Test
diff --git a/javascript/packages/turbo-lint/test/valid.html b/javascript/packages/turbo-lint/test/valid.html deleted file mode 100644 index 8e30faca1..000000000 --- a/javascript/packages/turbo-lint/test/valid.html +++ /dev/null @@ -1 +0,0 @@ -
Valid
diff --git a/javascript/packages/turbo-lint/tsconfig.json b/javascript/packages/turbo-lint/tsconfig.json deleted file mode 100644 index b36bf65cd..000000000 --- a/javascript/packages/turbo-lint/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "lib": ["ES2022"], - "outDir": "./dist", - "declaration": true, - "declarationDir": "./dist/types", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "sourceMap": true - }, - "include": ["src/**/*", "types/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/src/ast_nodes.c b/src/ast_nodes.c new file mode 100644 index 000000000..48d6a8ab5 --- /dev/null +++ b/src/ast_nodes.c @@ -0,0 +1,1010 @@ +// NOTE: This file is generated by the templates/template.rb script and should not +// be modified manually. See /Users/dani/oss/herb/templates/src/ast_nodes.c.erb + +#include +#include + +#include + +#include "include/analyzed_ruby.h" +#include "include/ast_node.h" +#include "include/ast_nodes.h" +#include "include/errors.h" +#include "include/token.h" +#include "include/util.h" +#include "include/util/hb_array.h" + + +AST_DOCUMENT_NODE_T* ast_document_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_DOCUMENT_NODE_T* document_node = malloc(sizeof(AST_DOCUMENT_NODE_T)); + + ast_node_init(&document_node->base, AST_DOCUMENT_NODE, start_position, end_position, errors); + + document_node->children = children; + + return document_node; +} + +AST_LITERAL_NODE_T* ast_literal_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_LITERAL_NODE_T* literal_node = malloc(sizeof(AST_LITERAL_NODE_T)); + + ast_node_init(&literal_node->base, AST_LITERAL_NODE, start_position, end_position, errors); + + literal_node->content = herb_strdup(content); + + return literal_node; +} + +AST_HTML_OPEN_TAG_NODE_T* ast_html_open_tag_node_init(token_T* tag_opening, token_T* tag_name, token_T* tag_closing, hb_array_T* children, bool is_void, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node = malloc(sizeof(AST_HTML_OPEN_TAG_NODE_T)); + + ast_node_init(&html_open_tag_node->base, AST_HTML_OPEN_TAG_NODE, start_position, end_position, errors); + + html_open_tag_node->tag_opening = token_copy(tag_opening); + html_open_tag_node->tag_name = token_copy(tag_name); + html_open_tag_node->tag_closing = token_copy(tag_closing); + html_open_tag_node->children = children; + html_open_tag_node->is_void = is_void; + + return html_open_tag_node; +} + +AST_HTML_CLOSE_TAG_NODE_T* ast_html_close_tag_node_init(token_T* tag_opening, token_T* tag_name, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node = malloc(sizeof(AST_HTML_CLOSE_TAG_NODE_T)); + + ast_node_init(&html_close_tag_node->base, AST_HTML_CLOSE_TAG_NODE, start_position, end_position, errors); + + html_close_tag_node->tag_opening = token_copy(tag_opening); + html_close_tag_node->tag_name = token_copy(tag_name); + html_close_tag_node->children = children; + html_close_tag_node->tag_closing = token_copy(tag_closing); + + return html_close_tag_node; +} + +AST_HTML_ELEMENT_NODE_T* ast_html_element_node_init(struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag, token_T* tag_name, hb_array_T* body, struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag, bool is_void, element_source_t source, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_ELEMENT_NODE_T* html_element_node = malloc(sizeof(AST_HTML_ELEMENT_NODE_T)); + + ast_node_init(&html_element_node->base, AST_HTML_ELEMENT_NODE, start_position, end_position, errors); + + html_element_node->open_tag = open_tag; + html_element_node->tag_name = token_copy(tag_name); + html_element_node->body = body; + html_element_node->close_tag = close_tag; + html_element_node->is_void = is_void; + html_element_node->source = source; + + return html_element_node; +} + +AST_HTML_ATTRIBUTE_VALUE_NODE_T* ast_html_attribute_value_node_init(token_T* open_quote, hb_array_T* children, token_T* close_quote, bool quoted, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node = malloc(sizeof(AST_HTML_ATTRIBUTE_VALUE_NODE_T)); + + ast_node_init(&html_attribute_value_node->base, AST_HTML_ATTRIBUTE_VALUE_NODE, start_position, end_position, errors); + + html_attribute_value_node->open_quote = token_copy(open_quote); + html_attribute_value_node->children = children; + html_attribute_value_node->close_quote = token_copy(close_quote); + html_attribute_value_node->quoted = quoted; + + return html_attribute_value_node; +} + +AST_HTML_ATTRIBUTE_NAME_NODE_T* ast_html_attribute_name_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node = malloc(sizeof(AST_HTML_ATTRIBUTE_NAME_NODE_T)); + + ast_node_init(&html_attribute_name_node->base, AST_HTML_ATTRIBUTE_NAME_NODE, start_position, end_position, errors); + + html_attribute_name_node->children = children; + + return html_attribute_name_node; +} + +AST_HTML_ATTRIBUTE_NODE_T* ast_html_attribute_node_init(struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name, token_T* equals, struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node = malloc(sizeof(AST_HTML_ATTRIBUTE_NODE_T)); + + ast_node_init(&html_attribute_node->base, AST_HTML_ATTRIBUTE_NODE, start_position, end_position, errors); + + html_attribute_node->name = name; + html_attribute_node->equals = token_copy(equals); + html_attribute_node->value = value; + + return html_attribute_node; +} + +AST_HTML_TEXT_NODE_T* ast_html_text_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_TEXT_NODE_T* html_text_node = malloc(sizeof(AST_HTML_TEXT_NODE_T)); + + ast_node_init(&html_text_node->base, AST_HTML_TEXT_NODE, start_position, end_position, errors); + + html_text_node->content = herb_strdup(content); + + return html_text_node; +} + +AST_HTML_COMMENT_NODE_T* ast_html_comment_node_init(token_T* comment_start, hb_array_T* children, token_T* comment_end, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_COMMENT_NODE_T* html_comment_node = malloc(sizeof(AST_HTML_COMMENT_NODE_T)); + + ast_node_init(&html_comment_node->base, AST_HTML_COMMENT_NODE, start_position, end_position, errors); + + html_comment_node->comment_start = token_copy(comment_start); + html_comment_node->children = children; + html_comment_node->comment_end = token_copy(comment_end); + + return html_comment_node; +} + +AST_HTML_DOCTYPE_NODE_T* ast_html_doctype_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_HTML_DOCTYPE_NODE_T* html_doctype_node = malloc(sizeof(AST_HTML_DOCTYPE_NODE_T)); + + ast_node_init(&html_doctype_node->base, AST_HTML_DOCTYPE_NODE, start_position, end_position, errors); + + html_doctype_node->tag_opening = token_copy(tag_opening); + html_doctype_node->children = children; + html_doctype_node->tag_closing = token_copy(tag_closing); + + return html_doctype_node; +} + +AST_XML_DECLARATION_NODE_T* ast_xml_declaration_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_XML_DECLARATION_NODE_T* xml_declaration_node = malloc(sizeof(AST_XML_DECLARATION_NODE_T)); + + ast_node_init(&xml_declaration_node->base, AST_XML_DECLARATION_NODE, start_position, end_position, errors); + + xml_declaration_node->tag_opening = token_copy(tag_opening); + xml_declaration_node->children = children; + xml_declaration_node->tag_closing = token_copy(tag_closing); + + return xml_declaration_node; +} + +AST_CDATA_NODE_T* ast_cdata_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_CDATA_NODE_T* cdata_node = malloc(sizeof(AST_CDATA_NODE_T)); + + ast_node_init(&cdata_node->base, AST_CDATA_NODE, start_position, end_position, errors); + + cdata_node->tag_opening = token_copy(tag_opening); + cdata_node->children = children; + cdata_node->tag_closing = token_copy(tag_closing); + + return cdata_node; +} + +AST_WHITESPACE_NODE_T* ast_whitespace_node_init(token_T* value, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_WHITESPACE_NODE_T* whitespace_node = malloc(sizeof(AST_WHITESPACE_NODE_T)); + + ast_node_init(&whitespace_node->base, AST_WHITESPACE_NODE, start_position, end_position, errors); + + whitespace_node->value = token_copy(value); + + return whitespace_node; +} + +AST_ERB_CONTENT_NODE_T* ast_erb_content_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, analyzed_ruby_T* analyzed_ruby, bool parsed, bool valid, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_CONTENT_NODE_T* erb_content_node = malloc(sizeof(AST_ERB_CONTENT_NODE_T)); + + ast_node_init(&erb_content_node->base, AST_ERB_CONTENT_NODE, start_position, end_position, errors); + + erb_content_node->tag_opening = token_copy(tag_opening); + erb_content_node->content = token_copy(content); + erb_content_node->tag_closing = token_copy(tag_closing); + erb_content_node->analyzed_ruby = analyzed_ruby; + erb_content_node->parsed = parsed; + erb_content_node->valid = valid; + + return erb_content_node; +} + +AST_ERB_END_NODE_T* ast_erb_end_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_END_NODE_T* erb_end_node = malloc(sizeof(AST_ERB_END_NODE_T)); + + ast_node_init(&erb_end_node->base, AST_ERB_END_NODE, start_position, end_position, errors); + + erb_end_node->tag_opening = token_copy(tag_opening); + erb_end_node->content = token_copy(content); + erb_end_node->tag_closing = token_copy(tag_closing); + + return erb_end_node; +} + +AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_ELSE_NODE_T* erb_else_node = malloc(sizeof(AST_ERB_ELSE_NODE_T)); + + ast_node_init(&erb_else_node->base, AST_ERB_ELSE_NODE, start_position, end_position, errors); + + erb_else_node->tag_opening = token_copy(tag_opening); + erb_else_node->content = token_copy(content); + erb_else_node->tag_closing = token_copy(tag_closing); + erb_else_node->statements = statements; + + return erb_else_node; +} + +AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_IF_NODE_T* erb_if_node = malloc(sizeof(AST_ERB_IF_NODE_T)); + + ast_node_init(&erb_if_node->base, AST_ERB_IF_NODE, start_position, end_position, errors); + + erb_if_node->tag_opening = token_copy(tag_opening); + erb_if_node->content = token_copy(content); + erb_if_node->tag_closing = token_copy(tag_closing); + erb_if_node->statements = statements; + erb_if_node->subsequent = subsequent; + erb_if_node->end_node = end_node; + + return erb_if_node; +} + +AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* body, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_BLOCK_NODE_T* erb_block_node = malloc(sizeof(AST_ERB_BLOCK_NODE_T)); + + ast_node_init(&erb_block_node->base, AST_ERB_BLOCK_NODE, start_position, end_position, errors); + + erb_block_node->tag_opening = token_copy(tag_opening); + erb_block_node->content = token_copy(content); + erb_block_node->tag_closing = token_copy(tag_closing); + erb_block_node->body = body; + erb_block_node->end_node = end_node; + + return erb_block_node; +} + +AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_WHEN_NODE_T* erb_when_node = malloc(sizeof(AST_ERB_WHEN_NODE_T)); + + ast_node_init(&erb_when_node->base, AST_ERB_WHEN_NODE, start_position, end_position, errors); + + erb_when_node->tag_opening = token_copy(tag_opening); + erb_when_node->content = token_copy(content); + erb_when_node->tag_closing = token_copy(tag_closing); + erb_when_node->statements = statements; + + return erb_when_node; +} + +AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_CASE_NODE_T* erb_case_node = malloc(sizeof(AST_ERB_CASE_NODE_T)); + + ast_node_init(&erb_case_node->base, AST_ERB_CASE_NODE, start_position, end_position, errors); + + erb_case_node->tag_opening = token_copy(tag_opening); + erb_case_node->content = token_copy(content); + erb_case_node->tag_closing = token_copy(tag_closing); + erb_case_node->children = children; + erb_case_node->conditions = conditions; + erb_case_node->else_clause = else_clause; + erb_case_node->end_node = end_node; + + return erb_case_node; +} + +AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = malloc(sizeof(AST_ERB_CASE_MATCH_NODE_T)); + + ast_node_init(&erb_case_match_node->base, AST_ERB_CASE_MATCH_NODE, start_position, end_position, errors); + + erb_case_match_node->tag_opening = token_copy(tag_opening); + erb_case_match_node->content = token_copy(content); + erb_case_match_node->tag_closing = token_copy(tag_closing); + erb_case_match_node->children = children; + erb_case_match_node->conditions = conditions; + erb_case_match_node->else_clause = else_clause; + erb_case_match_node->end_node = end_node; + + return erb_case_match_node; +} + +AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_WHILE_NODE_T* erb_while_node = malloc(sizeof(AST_ERB_WHILE_NODE_T)); + + ast_node_init(&erb_while_node->base, AST_ERB_WHILE_NODE, start_position, end_position, errors); + + erb_while_node->tag_opening = token_copy(tag_opening); + erb_while_node->content = token_copy(content); + erb_while_node->tag_closing = token_copy(tag_closing); + erb_while_node->statements = statements; + erb_while_node->end_node = end_node; + + return erb_while_node; +} + +AST_ERB_UNTIL_NODE_T* ast_erb_until_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_UNTIL_NODE_T* erb_until_node = malloc(sizeof(AST_ERB_UNTIL_NODE_T)); + + ast_node_init(&erb_until_node->base, AST_ERB_UNTIL_NODE, start_position, end_position, errors); + + erb_until_node->tag_opening = token_copy(tag_opening); + erb_until_node->content = token_copy(content); + erb_until_node->tag_closing = token_copy(tag_closing); + erb_until_node->statements = statements; + erb_until_node->end_node = end_node; + + return erb_until_node; +} + +AST_ERB_FOR_NODE_T* ast_erb_for_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_FOR_NODE_T* erb_for_node = malloc(sizeof(AST_ERB_FOR_NODE_T)); + + ast_node_init(&erb_for_node->base, AST_ERB_FOR_NODE, start_position, end_position, errors); + + erb_for_node->tag_opening = token_copy(tag_opening); + erb_for_node->content = token_copy(content); + erb_for_node->tag_closing = token_copy(tag_closing); + erb_for_node->statements = statements; + erb_for_node->end_node = end_node; + + return erb_for_node; +} + +AST_ERB_RESCUE_NODE_T* ast_erb_rescue_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* subsequent, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_RESCUE_NODE_T* erb_rescue_node = malloc(sizeof(AST_ERB_RESCUE_NODE_T)); + + ast_node_init(&erb_rescue_node->base, AST_ERB_RESCUE_NODE, start_position, end_position, errors); + + erb_rescue_node->tag_opening = token_copy(tag_opening); + erb_rescue_node->content = token_copy(content); + erb_rescue_node->tag_closing = token_copy(tag_closing); + erb_rescue_node->statements = statements; + erb_rescue_node->subsequent = subsequent; + + return erb_rescue_node; +} + +AST_ERB_ENSURE_NODE_T* ast_erb_ensure_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_ENSURE_NODE_T* erb_ensure_node = malloc(sizeof(AST_ERB_ENSURE_NODE_T)); + + ast_node_init(&erb_ensure_node->base, AST_ERB_ENSURE_NODE, start_position, end_position, errors); + + erb_ensure_node->tag_opening = token_copy(tag_opening); + erb_ensure_node->content = token_copy(content); + erb_ensure_node->tag_closing = token_copy(tag_closing); + erb_ensure_node->statements = statements; + + return erb_ensure_node; +} + +AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_BEGIN_NODE_T* erb_begin_node = malloc(sizeof(AST_ERB_BEGIN_NODE_T)); + + ast_node_init(&erb_begin_node->base, AST_ERB_BEGIN_NODE, start_position, end_position, errors); + + erb_begin_node->tag_opening = token_copy(tag_opening); + erb_begin_node->content = token_copy(content); + erb_begin_node->tag_closing = token_copy(tag_closing); + erb_begin_node->statements = statements; + erb_begin_node->rescue_clause = rescue_clause; + erb_begin_node->else_clause = else_clause; + erb_begin_node->ensure_clause = ensure_clause; + erb_begin_node->end_node = end_node; + + return erb_begin_node; +} + +AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_UNLESS_NODE_T* erb_unless_node = malloc(sizeof(AST_ERB_UNLESS_NODE_T)); + + ast_node_init(&erb_unless_node->base, AST_ERB_UNLESS_NODE, start_position, end_position, errors); + + erb_unless_node->tag_opening = token_copy(tag_opening); + erb_unless_node->content = token_copy(content); + erb_unless_node->tag_closing = token_copy(tag_closing); + erb_unless_node->statements = statements; + erb_unless_node->else_clause = else_clause; + erb_unless_node->end_node = end_node; + + return erb_unless_node; +} + +AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_YIELD_NODE_T* erb_yield_node = malloc(sizeof(AST_ERB_YIELD_NODE_T)); + + ast_node_init(&erb_yield_node->base, AST_ERB_YIELD_NODE, start_position, end_position, errors); + + erb_yield_node->tag_opening = token_copy(tag_opening); + erb_yield_node->content = token_copy(content); + erb_yield_node->tag_closing = token_copy(tag_closing); + + return erb_yield_node; +} + +AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { + AST_ERB_IN_NODE_T* erb_in_node = malloc(sizeof(AST_ERB_IN_NODE_T)); + + ast_node_init(&erb_in_node->base, AST_ERB_IN_NODE, start_position, end_position, errors); + + erb_in_node->tag_opening = token_copy(tag_opening); + erb_in_node->content = token_copy(content); + erb_in_node->tag_closing = token_copy(tag_closing); + erb_in_node->statements = statements; + + return erb_in_node; +} + +hb_string_T ast_node_type_to_string(AST_NODE_T* node) { + switch (node->type) { + case AST_DOCUMENT_NODE: return hb_string("AST_DOCUMENT_NODE"); + case AST_LITERAL_NODE: return hb_string("AST_LITERAL_NODE"); + case AST_HTML_OPEN_TAG_NODE: return hb_string("AST_HTML_OPEN_TAG_NODE"); + case AST_HTML_CLOSE_TAG_NODE: return hb_string("AST_HTML_CLOSE_TAG_NODE"); + case AST_HTML_ELEMENT_NODE: return hb_string("AST_HTML_ELEMENT_NODE"); + case AST_HTML_ATTRIBUTE_VALUE_NODE: return hb_string("AST_HTML_ATTRIBUTE_VALUE_NODE"); + case AST_HTML_ATTRIBUTE_NAME_NODE: return hb_string("AST_HTML_ATTRIBUTE_NAME_NODE"); + case AST_HTML_ATTRIBUTE_NODE: return hb_string("AST_HTML_ATTRIBUTE_NODE"); + case AST_HTML_TEXT_NODE: return hb_string("AST_HTML_TEXT_NODE"); + case AST_HTML_COMMENT_NODE: return hb_string("AST_HTML_COMMENT_NODE"); + case AST_HTML_DOCTYPE_NODE: return hb_string("AST_HTML_DOCTYPE_NODE"); + case AST_XML_DECLARATION_NODE: return hb_string("AST_XML_DECLARATION_NODE"); + case AST_CDATA_NODE: return hb_string("AST_CDATA_NODE"); + case AST_WHITESPACE_NODE: return hb_string("AST_WHITESPACE_NODE"); + case AST_ERB_CONTENT_NODE: return hb_string("AST_ERB_CONTENT_NODE"); + case AST_ERB_END_NODE: return hb_string("AST_ERB_END_NODE"); + case AST_ERB_ELSE_NODE: return hb_string("AST_ERB_ELSE_NODE"); + case AST_ERB_IF_NODE: return hb_string("AST_ERB_IF_NODE"); + case AST_ERB_BLOCK_NODE: return hb_string("AST_ERB_BLOCK_NODE"); + case AST_ERB_WHEN_NODE: return hb_string("AST_ERB_WHEN_NODE"); + case AST_ERB_CASE_NODE: return hb_string("AST_ERB_CASE_NODE"); + case AST_ERB_CASE_MATCH_NODE: return hb_string("AST_ERB_CASE_MATCH_NODE"); + case AST_ERB_WHILE_NODE: return hb_string("AST_ERB_WHILE_NODE"); + case AST_ERB_UNTIL_NODE: return hb_string("AST_ERB_UNTIL_NODE"); + case AST_ERB_FOR_NODE: return hb_string("AST_ERB_FOR_NODE"); + case AST_ERB_RESCUE_NODE: return hb_string("AST_ERB_RESCUE_NODE"); + case AST_ERB_ENSURE_NODE: return hb_string("AST_ERB_ENSURE_NODE"); + case AST_ERB_BEGIN_NODE: return hb_string("AST_ERB_BEGIN_NODE"); + case AST_ERB_UNLESS_NODE: return hb_string("AST_ERB_UNLESS_NODE"); + case AST_ERB_YIELD_NODE: return hb_string("AST_ERB_YIELD_NODE"); + case AST_ERB_IN_NODE: return hb_string("AST_ERB_IN_NODE"); + } + + return hb_string("Unknown ast_node_type_T"); +} + +hb_string_T ast_node_human_type(AST_NODE_T* node) { + switch (node->type) { + case AST_DOCUMENT_NODE: return hb_string("DocumentNode"); + case AST_LITERAL_NODE: return hb_string("LiteralNode"); + case AST_HTML_OPEN_TAG_NODE: return hb_string("HTMLOpenTagNode"); + case AST_HTML_CLOSE_TAG_NODE: return hb_string("HTMLCloseTagNode"); + case AST_HTML_ELEMENT_NODE: return hb_string("HTMLElementNode"); + case AST_HTML_ATTRIBUTE_VALUE_NODE: return hb_string("HTMLAttributeValueNode"); + case AST_HTML_ATTRIBUTE_NAME_NODE: return hb_string("HTMLAttributeNameNode"); + case AST_HTML_ATTRIBUTE_NODE: return hb_string("HTMLAttributeNode"); + case AST_HTML_TEXT_NODE: return hb_string("HTMLTextNode"); + case AST_HTML_COMMENT_NODE: return hb_string("HTMLCommentNode"); + case AST_HTML_DOCTYPE_NODE: return hb_string("HTMLDoctypeNode"); + case AST_XML_DECLARATION_NODE: return hb_string("XMLDeclarationNode"); + case AST_CDATA_NODE: return hb_string("CDATANode"); + case AST_WHITESPACE_NODE: return hb_string("WhitespaceNode"); + case AST_ERB_CONTENT_NODE: return hb_string("ERBContentNode"); + case AST_ERB_END_NODE: return hb_string("ERBEndNode"); + case AST_ERB_ELSE_NODE: return hb_string("ERBElseNode"); + case AST_ERB_IF_NODE: return hb_string("ERBIfNode"); + case AST_ERB_BLOCK_NODE: return hb_string("ERBBlockNode"); + case AST_ERB_WHEN_NODE: return hb_string("ERBWhenNode"); + case AST_ERB_CASE_NODE: return hb_string("ERBCaseNode"); + case AST_ERB_CASE_MATCH_NODE: return hb_string("ERBCaseMatchNode"); + case AST_ERB_WHILE_NODE: return hb_string("ERBWhileNode"); + case AST_ERB_UNTIL_NODE: return hb_string("ERBUntilNode"); + case AST_ERB_FOR_NODE: return hb_string("ERBForNode"); + case AST_ERB_RESCUE_NODE: return hb_string("ERBRescueNode"); + case AST_ERB_ENSURE_NODE: return hb_string("ERBEnsureNode"); + case AST_ERB_BEGIN_NODE: return hb_string("ERBBeginNode"); + case AST_ERB_UNLESS_NODE: return hb_string("ERBUnlessNode"); + case AST_ERB_YIELD_NODE: return hb_string("ERBYieldNode"); + case AST_ERB_IN_NODE: return hb_string("ERBInNode"); + } + + return hb_string("Unknown ast_node_type_T"); +} + +void ast_free_base_node(AST_NODE_T* node) { + if (node == NULL) { return; } + + if (node->errors) { + for (size_t i = 0; i < hb_array_size(node->errors); i++) { + ERROR_T* child = hb_array_get(node->errors, i); + if (child != NULL) { error_free(child); } + } + + hb_array_free(&node->errors); + } + + free(node); +} + + +static void ast_free_document_node(AST_DOCUMENT_NODE_T* document_node) { + if (document_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(document_node->children); i++) { + AST_NODE_T* child = hb_array_get(document_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&document_node->children); + } + + ast_free_base_node(&document_node->base); +} + +static void ast_free_literal_node(AST_LITERAL_NODE_T* literal_node) { + if (literal_node->content != NULL) { free((char*) literal_node->content); } + + ast_free_base_node(&literal_node->base); +} + +static void ast_free_html_open_tag_node(AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node) { + if (html_open_tag_node->tag_opening != NULL) { token_free(html_open_tag_node->tag_opening); } + if (html_open_tag_node->tag_name != NULL) { token_free(html_open_tag_node->tag_name); } + if (html_open_tag_node->tag_closing != NULL) { token_free(html_open_tag_node->tag_closing); } + if (html_open_tag_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_open_tag_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_open_tag_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_open_tag_node->children); + } + + ast_free_base_node(&html_open_tag_node->base); +} + +static void ast_free_html_close_tag_node(AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node) { + if (html_close_tag_node->tag_opening != NULL) { token_free(html_close_tag_node->tag_opening); } + if (html_close_tag_node->tag_name != NULL) { token_free(html_close_tag_node->tag_name); } + if (html_close_tag_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_close_tag_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_close_tag_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_close_tag_node->children); + } + if (html_close_tag_node->tag_closing != NULL) { token_free(html_close_tag_node->tag_closing); } + + ast_free_base_node(&html_close_tag_node->base); +} + +static void ast_free_html_element_node(AST_HTML_ELEMENT_NODE_T* html_element_node) { + ast_node_free((AST_NODE_T*) html_element_node->open_tag); + if (html_element_node->tag_name != NULL) { token_free(html_element_node->tag_name); } + if (html_element_node->body != NULL) { + for (size_t i = 0; i < hb_array_size(html_element_node->body); i++) { + AST_NODE_T* child = hb_array_get(html_element_node->body, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_element_node->body); + } + ast_node_free((AST_NODE_T*) html_element_node->close_tag); + + ast_free_base_node(&html_element_node->base); +} + +static void ast_free_html_attribute_value_node(AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node) { + if (html_attribute_value_node->open_quote != NULL) { token_free(html_attribute_value_node->open_quote); } + if (html_attribute_value_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_attribute_value_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_attribute_value_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_attribute_value_node->children); + } + if (html_attribute_value_node->close_quote != NULL) { token_free(html_attribute_value_node->close_quote); } + + ast_free_base_node(&html_attribute_value_node->base); +} + +static void ast_free_html_attribute_name_node(AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node) { + if (html_attribute_name_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_attribute_name_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_attribute_name_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_attribute_name_node->children); + } + + ast_free_base_node(&html_attribute_name_node->base); +} + +static void ast_free_html_attribute_node(AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node) { + ast_node_free((AST_NODE_T*) html_attribute_node->name); + if (html_attribute_node->equals != NULL) { token_free(html_attribute_node->equals); } + ast_node_free((AST_NODE_T*) html_attribute_node->value); + + ast_free_base_node(&html_attribute_node->base); +} + +static void ast_free_html_text_node(AST_HTML_TEXT_NODE_T* html_text_node) { + if (html_text_node->content != NULL) { free((char*) html_text_node->content); } + + ast_free_base_node(&html_text_node->base); +} + +static void ast_free_html_comment_node(AST_HTML_COMMENT_NODE_T* html_comment_node) { + if (html_comment_node->comment_start != NULL) { token_free(html_comment_node->comment_start); } + if (html_comment_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_comment_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_comment_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_comment_node->children); + } + if (html_comment_node->comment_end != NULL) { token_free(html_comment_node->comment_end); } + + ast_free_base_node(&html_comment_node->base); +} + +static void ast_free_html_doctype_node(AST_HTML_DOCTYPE_NODE_T* html_doctype_node) { + if (html_doctype_node->tag_opening != NULL) { token_free(html_doctype_node->tag_opening); } + if (html_doctype_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(html_doctype_node->children); i++) { + AST_NODE_T* child = hb_array_get(html_doctype_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&html_doctype_node->children); + } + if (html_doctype_node->tag_closing != NULL) { token_free(html_doctype_node->tag_closing); } + + ast_free_base_node(&html_doctype_node->base); +} + +static void ast_free_xml_declaration_node(AST_XML_DECLARATION_NODE_T* xml_declaration_node) { + if (xml_declaration_node->tag_opening != NULL) { token_free(xml_declaration_node->tag_opening); } + if (xml_declaration_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(xml_declaration_node->children); i++) { + AST_NODE_T* child = hb_array_get(xml_declaration_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&xml_declaration_node->children); + } + if (xml_declaration_node->tag_closing != NULL) { token_free(xml_declaration_node->tag_closing); } + + ast_free_base_node(&xml_declaration_node->base); +} + +static void ast_free_cdata_node(AST_CDATA_NODE_T* cdata_node) { + if (cdata_node->tag_opening != NULL) { token_free(cdata_node->tag_opening); } + if (cdata_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(cdata_node->children); i++) { + AST_NODE_T* child = hb_array_get(cdata_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&cdata_node->children); + } + if (cdata_node->tag_closing != NULL) { token_free(cdata_node->tag_closing); } + + ast_free_base_node(&cdata_node->base); +} + +static void ast_free_whitespace_node(AST_WHITESPACE_NODE_T* whitespace_node) { + if (whitespace_node->value != NULL) { token_free(whitespace_node->value); } + + ast_free_base_node(&whitespace_node->base); +} + +static void ast_free_erb_content_node(AST_ERB_CONTENT_NODE_T* erb_content_node) { + if (erb_content_node->tag_opening != NULL) { token_free(erb_content_node->tag_opening); } + if (erb_content_node->content != NULL) { token_free(erb_content_node->content); } + if (erb_content_node->tag_closing != NULL) { token_free(erb_content_node->tag_closing); } + if (erb_content_node->analyzed_ruby != NULL) { + free_analyzed_ruby(erb_content_node->analyzed_ruby); + } + + ast_free_base_node(&erb_content_node->base); +} + +static void ast_free_erb_end_node(AST_ERB_END_NODE_T* erb_end_node) { + if (erb_end_node->tag_opening != NULL) { token_free(erb_end_node->tag_opening); } + if (erb_end_node->content != NULL) { token_free(erb_end_node->content); } + if (erb_end_node->tag_closing != NULL) { token_free(erb_end_node->tag_closing); } + + ast_free_base_node(&erb_end_node->base); +} + +static void ast_free_erb_else_node(AST_ERB_ELSE_NODE_T* erb_else_node) { + if (erb_else_node->tag_opening != NULL) { token_free(erb_else_node->tag_opening); } + if (erb_else_node->content != NULL) { token_free(erb_else_node->content); } + if (erb_else_node->tag_closing != NULL) { token_free(erb_else_node->tag_closing); } + if (erb_else_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_else_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_else_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_else_node->statements); + } + + ast_free_base_node(&erb_else_node->base); +} + +static void ast_free_erb_if_node(AST_ERB_IF_NODE_T* erb_if_node) { + if (erb_if_node->tag_opening != NULL) { token_free(erb_if_node->tag_opening); } + if (erb_if_node->content != NULL) { token_free(erb_if_node->content); } + if (erb_if_node->tag_closing != NULL) { token_free(erb_if_node->tag_closing); } + if (erb_if_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_if_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_if_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_if_node->statements); + } + ast_node_free((AST_NODE_T*) erb_if_node->subsequent); + ast_node_free((AST_NODE_T*) erb_if_node->end_node); + + ast_free_base_node(&erb_if_node->base); +} + +static void ast_free_erb_block_node(AST_ERB_BLOCK_NODE_T* erb_block_node) { + if (erb_block_node->tag_opening != NULL) { token_free(erb_block_node->tag_opening); } + if (erb_block_node->content != NULL) { token_free(erb_block_node->content); } + if (erb_block_node->tag_closing != NULL) { token_free(erb_block_node->tag_closing); } + if (erb_block_node->body != NULL) { + for (size_t i = 0; i < hb_array_size(erb_block_node->body); i++) { + AST_NODE_T* child = hb_array_get(erb_block_node->body, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_block_node->body); + } + ast_node_free((AST_NODE_T*) erb_block_node->end_node); + + ast_free_base_node(&erb_block_node->base); +} + +static void ast_free_erb_when_node(AST_ERB_WHEN_NODE_T* erb_when_node) { + if (erb_when_node->tag_opening != NULL) { token_free(erb_when_node->tag_opening); } + if (erb_when_node->content != NULL) { token_free(erb_when_node->content); } + if (erb_when_node->tag_closing != NULL) { token_free(erb_when_node->tag_closing); } + if (erb_when_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_when_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_when_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_when_node->statements); + } + + ast_free_base_node(&erb_when_node->base); +} + +static void ast_free_erb_case_node(AST_ERB_CASE_NODE_T* erb_case_node) { + if (erb_case_node->tag_opening != NULL) { token_free(erb_case_node->tag_opening); } + if (erb_case_node->content != NULL) { token_free(erb_case_node->content); } + if (erb_case_node->tag_closing != NULL) { token_free(erb_case_node->tag_closing); } + if (erb_case_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(erb_case_node->children); i++) { + AST_NODE_T* child = hb_array_get(erb_case_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_case_node->children); + } + if (erb_case_node->conditions != NULL) { + for (size_t i = 0; i < hb_array_size(erb_case_node->conditions); i++) { + AST_NODE_T* child = hb_array_get(erb_case_node->conditions, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_case_node->conditions); + } + ast_node_free((AST_NODE_T*) erb_case_node->else_clause); + ast_node_free((AST_NODE_T*) erb_case_node->end_node); + + ast_free_base_node(&erb_case_node->base); +} + +static void ast_free_erb_case_match_node(AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node) { + if (erb_case_match_node->tag_opening != NULL) { token_free(erb_case_match_node->tag_opening); } + if (erb_case_match_node->content != NULL) { token_free(erb_case_match_node->content); } + if (erb_case_match_node->tag_closing != NULL) { token_free(erb_case_match_node->tag_closing); } + if (erb_case_match_node->children != NULL) { + for (size_t i = 0; i < hb_array_size(erb_case_match_node->children); i++) { + AST_NODE_T* child = hb_array_get(erb_case_match_node->children, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_case_match_node->children); + } + if (erb_case_match_node->conditions != NULL) { + for (size_t i = 0; i < hb_array_size(erb_case_match_node->conditions); i++) { + AST_NODE_T* child = hb_array_get(erb_case_match_node->conditions, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_case_match_node->conditions); + } + ast_node_free((AST_NODE_T*) erb_case_match_node->else_clause); + ast_node_free((AST_NODE_T*) erb_case_match_node->end_node); + + ast_free_base_node(&erb_case_match_node->base); +} + +static void ast_free_erb_while_node(AST_ERB_WHILE_NODE_T* erb_while_node) { + if (erb_while_node->tag_opening != NULL) { token_free(erb_while_node->tag_opening); } + if (erb_while_node->content != NULL) { token_free(erb_while_node->content); } + if (erb_while_node->tag_closing != NULL) { token_free(erb_while_node->tag_closing); } + if (erb_while_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_while_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_while_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_while_node->statements); + } + ast_node_free((AST_NODE_T*) erb_while_node->end_node); + + ast_free_base_node(&erb_while_node->base); +} + +static void ast_free_erb_until_node(AST_ERB_UNTIL_NODE_T* erb_until_node) { + if (erb_until_node->tag_opening != NULL) { token_free(erb_until_node->tag_opening); } + if (erb_until_node->content != NULL) { token_free(erb_until_node->content); } + if (erb_until_node->tag_closing != NULL) { token_free(erb_until_node->tag_closing); } + if (erb_until_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_until_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_until_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_until_node->statements); + } + ast_node_free((AST_NODE_T*) erb_until_node->end_node); + + ast_free_base_node(&erb_until_node->base); +} + +static void ast_free_erb_for_node(AST_ERB_FOR_NODE_T* erb_for_node) { + if (erb_for_node->tag_opening != NULL) { token_free(erb_for_node->tag_opening); } + if (erb_for_node->content != NULL) { token_free(erb_for_node->content); } + if (erb_for_node->tag_closing != NULL) { token_free(erb_for_node->tag_closing); } + if (erb_for_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_for_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_for_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_for_node->statements); + } + ast_node_free((AST_NODE_T*) erb_for_node->end_node); + + ast_free_base_node(&erb_for_node->base); +} + +static void ast_free_erb_rescue_node(AST_ERB_RESCUE_NODE_T* erb_rescue_node) { + if (erb_rescue_node->tag_opening != NULL) { token_free(erb_rescue_node->tag_opening); } + if (erb_rescue_node->content != NULL) { token_free(erb_rescue_node->content); } + if (erb_rescue_node->tag_closing != NULL) { token_free(erb_rescue_node->tag_closing); } + if (erb_rescue_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_rescue_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_rescue_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_rescue_node->statements); + } + ast_node_free((AST_NODE_T*) erb_rescue_node->subsequent); + + ast_free_base_node(&erb_rescue_node->base); +} + +static void ast_free_erb_ensure_node(AST_ERB_ENSURE_NODE_T* erb_ensure_node) { + if (erb_ensure_node->tag_opening != NULL) { token_free(erb_ensure_node->tag_opening); } + if (erb_ensure_node->content != NULL) { token_free(erb_ensure_node->content); } + if (erb_ensure_node->tag_closing != NULL) { token_free(erb_ensure_node->tag_closing); } + if (erb_ensure_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_ensure_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_ensure_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_ensure_node->statements); + } + + ast_free_base_node(&erb_ensure_node->base); +} + +static void ast_free_erb_begin_node(AST_ERB_BEGIN_NODE_T* erb_begin_node) { + if (erb_begin_node->tag_opening != NULL) { token_free(erb_begin_node->tag_opening); } + if (erb_begin_node->content != NULL) { token_free(erb_begin_node->content); } + if (erb_begin_node->tag_closing != NULL) { token_free(erb_begin_node->tag_closing); } + if (erb_begin_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_begin_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_begin_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_begin_node->statements); + } + ast_node_free((AST_NODE_T*) erb_begin_node->rescue_clause); + ast_node_free((AST_NODE_T*) erb_begin_node->else_clause); + ast_node_free((AST_NODE_T*) erb_begin_node->ensure_clause); + ast_node_free((AST_NODE_T*) erb_begin_node->end_node); + + ast_free_base_node(&erb_begin_node->base); +} + +static void ast_free_erb_unless_node(AST_ERB_UNLESS_NODE_T* erb_unless_node) { + if (erb_unless_node->tag_opening != NULL) { token_free(erb_unless_node->tag_opening); } + if (erb_unless_node->content != NULL) { token_free(erb_unless_node->content); } + if (erb_unless_node->tag_closing != NULL) { token_free(erb_unless_node->tag_closing); } + if (erb_unless_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_unless_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_unless_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_unless_node->statements); + } + ast_node_free((AST_NODE_T*) erb_unless_node->else_clause); + ast_node_free((AST_NODE_T*) erb_unless_node->end_node); + + ast_free_base_node(&erb_unless_node->base); +} + +static void ast_free_erb_yield_node(AST_ERB_YIELD_NODE_T* erb_yield_node) { + if (erb_yield_node->tag_opening != NULL) { token_free(erb_yield_node->tag_opening); } + if (erb_yield_node->content != NULL) { token_free(erb_yield_node->content); } + if (erb_yield_node->tag_closing != NULL) { token_free(erb_yield_node->tag_closing); } + + ast_free_base_node(&erb_yield_node->base); +} + +static void ast_free_erb_in_node(AST_ERB_IN_NODE_T* erb_in_node) { + if (erb_in_node->tag_opening != NULL) { token_free(erb_in_node->tag_opening); } + if (erb_in_node->content != NULL) { token_free(erb_in_node->content); } + if (erb_in_node->tag_closing != NULL) { token_free(erb_in_node->tag_closing); } + if (erb_in_node->statements != NULL) { + for (size_t i = 0; i < hb_array_size(erb_in_node->statements); i++) { + AST_NODE_T* child = hb_array_get(erb_in_node->statements, i); + if (child) { ast_node_free(child); } + } + + hb_array_free(&erb_in_node->statements); + } + + ast_free_base_node(&erb_in_node->base); +} + +void ast_node_free(AST_NODE_T* node) { + if (!node) { return; } + + switch (node->type) { + case AST_DOCUMENT_NODE: ast_free_document_node((AST_DOCUMENT_NODE_T*) node); break; + case AST_LITERAL_NODE: ast_free_literal_node((AST_LITERAL_NODE_T*) node); break; + case AST_HTML_OPEN_TAG_NODE: ast_free_html_open_tag_node((AST_HTML_OPEN_TAG_NODE_T*) node); break; + case AST_HTML_CLOSE_TAG_NODE: ast_free_html_close_tag_node((AST_HTML_CLOSE_TAG_NODE_T*) node); break; + case AST_HTML_ELEMENT_NODE: ast_free_html_element_node((AST_HTML_ELEMENT_NODE_T*) node); break; + case AST_HTML_ATTRIBUTE_VALUE_NODE: ast_free_html_attribute_value_node((AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node); break; + case AST_HTML_ATTRIBUTE_NAME_NODE: ast_free_html_attribute_name_node((AST_HTML_ATTRIBUTE_NAME_NODE_T*) node); break; + case AST_HTML_ATTRIBUTE_NODE: ast_free_html_attribute_node((AST_HTML_ATTRIBUTE_NODE_T*) node); break; + case AST_HTML_TEXT_NODE: ast_free_html_text_node((AST_HTML_TEXT_NODE_T*) node); break; + case AST_HTML_COMMENT_NODE: ast_free_html_comment_node((AST_HTML_COMMENT_NODE_T*) node); break; + case AST_HTML_DOCTYPE_NODE: ast_free_html_doctype_node((AST_HTML_DOCTYPE_NODE_T*) node); break; + case AST_XML_DECLARATION_NODE: ast_free_xml_declaration_node((AST_XML_DECLARATION_NODE_T*) node); break; + case AST_CDATA_NODE: ast_free_cdata_node((AST_CDATA_NODE_T*) node); break; + case AST_WHITESPACE_NODE: ast_free_whitespace_node((AST_WHITESPACE_NODE_T*) node); break; + case AST_ERB_CONTENT_NODE: ast_free_erb_content_node((AST_ERB_CONTENT_NODE_T*) node); break; + case AST_ERB_END_NODE: ast_free_erb_end_node((AST_ERB_END_NODE_T*) node); break; + case AST_ERB_ELSE_NODE: ast_free_erb_else_node((AST_ERB_ELSE_NODE_T*) node); break; + case AST_ERB_IF_NODE: ast_free_erb_if_node((AST_ERB_IF_NODE_T*) node); break; + case AST_ERB_BLOCK_NODE: ast_free_erb_block_node((AST_ERB_BLOCK_NODE_T*) node); break; + case AST_ERB_WHEN_NODE: ast_free_erb_when_node((AST_ERB_WHEN_NODE_T*) node); break; + case AST_ERB_CASE_NODE: ast_free_erb_case_node((AST_ERB_CASE_NODE_T*) node); break; + case AST_ERB_CASE_MATCH_NODE: ast_free_erb_case_match_node((AST_ERB_CASE_MATCH_NODE_T*) node); break; + case AST_ERB_WHILE_NODE: ast_free_erb_while_node((AST_ERB_WHILE_NODE_T*) node); break; + case AST_ERB_UNTIL_NODE: ast_free_erb_until_node((AST_ERB_UNTIL_NODE_T*) node); break; + case AST_ERB_FOR_NODE: ast_free_erb_for_node((AST_ERB_FOR_NODE_T*) node); break; + case AST_ERB_RESCUE_NODE: ast_free_erb_rescue_node((AST_ERB_RESCUE_NODE_T*) node); break; + case AST_ERB_ENSURE_NODE: ast_free_erb_ensure_node((AST_ERB_ENSURE_NODE_T*) node); break; + case AST_ERB_BEGIN_NODE: ast_free_erb_begin_node((AST_ERB_BEGIN_NODE_T*) node); break; + case AST_ERB_UNLESS_NODE: ast_free_erb_unless_node((AST_ERB_UNLESS_NODE_T*) node); break; + case AST_ERB_YIELD_NODE: ast_free_erb_yield_node((AST_ERB_YIELD_NODE_T*) node); break; + case AST_ERB_IN_NODE: ast_free_erb_in_node((AST_ERB_IN_NODE_T*) node); break; + } +} diff --git a/src/ast_pretty_print.c b/src/ast_pretty_print.c new file mode 100644 index 000000000..05a08a93a --- /dev/null +++ b/src/ast_pretty_print.c @@ -0,0 +1,657 @@ +// NOTE: This file is generated by the templates/template.rb script and should not +// be modified manually. See /Users/dani/oss/herb/templates/src/ast_pretty_print.c.erb + +#include "include/ast_node.h" +#include "include/ast_nodes.h" +#include "include/errors.h" +#include "include/pretty_print.h" +#include "include/token_struct.h" +#include "include/util.h" +#include "include/util/hb_buffer.h" + +#include +#include +#include + +void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) { + if (!node) { return; } + + bool print_location = true; + + hb_buffer_append(buffer, "@ "); + hb_buffer_append_string(buffer, ast_node_human_type(node)); + hb_buffer_append(buffer, " "); + + if (print_location) { pretty_print_location(node->location, buffer); } + + hb_buffer_append(buffer, "\n"); + + switch (node->type) { + case AST_DOCUMENT_NODE: { + const AST_DOCUMENT_NODE_T* document_node = (AST_DOCUMENT_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), document_node->children, indent, relative_indent, true, buffer); + } break; + + case AST_LITERAL_NODE: { + const AST_LITERAL_NODE_T* literal_node = (AST_LITERAL_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_string_property(hb_string(literal_node->content), hb_string("content"), indent, relative_indent, true, buffer); + } break; + + case AST_HTML_OPEN_TAG_NODE: { + const AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node = (AST_HTML_OPEN_TAG_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(html_open_tag_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(html_open_tag_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); + pretty_print_token_property(html_open_tag_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_open_tag_node->children, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("is_void"), html_open_tag_node->is_void, indent, relative_indent, true, buffer); + } break; + + case AST_HTML_CLOSE_TAG_NODE: { + const AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node = (AST_HTML_CLOSE_TAG_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(html_close_tag_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(html_close_tag_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_close_tag_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(html_close_tag_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_HTML_ELEMENT_NODE: { + const AST_HTML_ELEMENT_NODE_T* html_element_node = (AST_HTML_ELEMENT_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("open_tag"), indent, relative_indent, false, buffer); + + if (html_element_node->open_tag) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) html_element_node->open_tag, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + pretty_print_token_property(html_element_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("body"), html_element_node->body, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("close_tag"), indent, relative_indent, false, buffer); + + if (html_element_node->close_tag) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) html_element_node->close_tag, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + pretty_print_boolean_property(hb_string("is_void"), html_element_node->is_void, indent, relative_indent, false, buffer); + pretty_print_string_property(element_source_to_string(html_element_node->source), hb_string("source"), indent, relative_indent, true, buffer); + } break; + + case AST_HTML_ATTRIBUTE_VALUE_NODE: { + const AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node = (AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(html_attribute_value_node->open_quote, hb_string("open_quote"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_attribute_value_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(html_attribute_value_node->close_quote, hb_string("close_quote"), indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("quoted"), html_attribute_value_node->quoted, indent, relative_indent, true, buffer); + } break; + + case AST_HTML_ATTRIBUTE_NAME_NODE: { + const AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node = (AST_HTML_ATTRIBUTE_NAME_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_attribute_name_node->children, indent, relative_indent, true, buffer); + } break; + + case AST_HTML_ATTRIBUTE_NODE: { + const AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node = (AST_HTML_ATTRIBUTE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("name"), indent, relative_indent, false, buffer); + + if (html_attribute_node->name) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) html_attribute_node->name, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + pretty_print_token_property(html_attribute_node->equals, hb_string("equals"), indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("value"), indent, relative_indent, true, buffer); + + if (html_attribute_node->value) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) html_attribute_node->value, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_HTML_TEXT_NODE: { + const AST_HTML_TEXT_NODE_T* html_text_node = (AST_HTML_TEXT_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_string_property(hb_string(html_text_node->content), hb_string("content"), indent, relative_indent, true, buffer); + } break; + + case AST_HTML_COMMENT_NODE: { + const AST_HTML_COMMENT_NODE_T* html_comment_node = (AST_HTML_COMMENT_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(html_comment_node->comment_start, hb_string("comment_start"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_comment_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(html_comment_node->comment_end, hb_string("comment_end"), indent, relative_indent, true, buffer); + } break; + + case AST_HTML_DOCTYPE_NODE: { + const AST_HTML_DOCTYPE_NODE_T* html_doctype_node = (AST_HTML_DOCTYPE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(html_doctype_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), html_doctype_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(html_doctype_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_XML_DECLARATION_NODE: { + const AST_XML_DECLARATION_NODE_T* xml_declaration_node = (AST_XML_DECLARATION_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(xml_declaration_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), xml_declaration_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(xml_declaration_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_CDATA_NODE: { + const AST_CDATA_NODE_T* cdata_node = (AST_CDATA_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(cdata_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), cdata_node->children, indent, relative_indent, false, buffer); + pretty_print_token_property(cdata_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_WHITESPACE_NODE: { + const AST_WHITESPACE_NODE_T* whitespace_node = (AST_WHITESPACE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(whitespace_node->value, hb_string("value"), indent, relative_indent, true, buffer); + } break; + + case AST_ERB_CONTENT_NODE: { + const AST_ERB_CONTENT_NODE_T* erb_content_node = (AST_ERB_CONTENT_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_content_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_content_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_content_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + if (erb_content_node->analyzed_ruby) { + pretty_print_boolean_property(hb_string("if_node"), erb_content_node->analyzed_ruby->has_if_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("elsif_node"), erb_content_node->analyzed_ruby->has_elsif_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("else_node"), erb_content_node->analyzed_ruby->has_else_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("end"), erb_content_node->analyzed_ruby->has_end, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("block_node"), erb_content_node->analyzed_ruby->has_block_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("block_closing"), erb_content_node->analyzed_ruby->has_block_closing, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("case_node"), erb_content_node->analyzed_ruby->has_case_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("when_node"), erb_content_node->analyzed_ruby->has_when_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("for_node"), erb_content_node->analyzed_ruby->has_for_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("while_node"), erb_content_node->analyzed_ruby->has_while_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("until_node"), erb_content_node->analyzed_ruby->has_until_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("begin_node"), erb_content_node->analyzed_ruby->has_begin_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("rescue_node"), erb_content_node->analyzed_ruby->has_rescue_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("ensure_node"), erb_content_node->analyzed_ruby->has_ensure_node, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("unless_node"), erb_content_node->analyzed_ruby->has_unless_node, indent, relative_indent, false, buffer); + } else { + pretty_print_label(hb_string("analyzed_ruby"), indent, relative_indent, false, buffer); + hb_buffer_append(buffer, " ∅\n"); + } + + pretty_print_boolean_property(hb_string("parsed"), erb_content_node->parsed, indent, relative_indent, false, buffer); + pretty_print_boolean_property(hb_string("valid"), erb_content_node->valid, indent, relative_indent, true, buffer); + } break; + + case AST_ERB_END_NODE: { + const AST_ERB_END_NODE_T* erb_end_node = (AST_ERB_END_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_end_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_end_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_end_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_ERB_ELSE_NODE: { + const AST_ERB_ELSE_NODE_T* erb_else_node = (AST_ERB_ELSE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_else_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_else_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_else_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_else_node->statements, indent, relative_indent, true, buffer); + } break; + + case AST_ERB_IF_NODE: { + const AST_ERB_IF_NODE_T* erb_if_node = (AST_ERB_IF_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_if_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_if_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_if_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_if_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("subsequent"), indent, relative_indent, false, buffer); + + if (erb_if_node->subsequent) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_if_node->subsequent, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_if_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_if_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_BLOCK_NODE: { + const AST_ERB_BLOCK_NODE_T* erb_block_node = (AST_ERB_BLOCK_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_block_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_block_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_block_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("body"), erb_block_node->body, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_block_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_block_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_WHEN_NODE: { + const AST_ERB_WHEN_NODE_T* erb_when_node = (AST_ERB_WHEN_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_when_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_when_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_when_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_when_node->statements, indent, relative_indent, true, buffer); + } break; + + case AST_ERB_CASE_NODE: { + const AST_ERB_CASE_NODE_T* erb_case_node = (AST_ERB_CASE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), erb_case_node->children, indent, relative_indent, false, buffer); + pretty_print_array(hb_string("conditions"), erb_case_node->conditions, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); + + if (erb_case_node->else_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_case_node->else_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_case_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_case_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_CASE_MATCH_NODE: { + const AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = (AST_ERB_CASE_MATCH_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_match_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_match_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_case_match_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("children"), erb_case_match_node->children, indent, relative_indent, false, buffer); + pretty_print_array(hb_string("conditions"), erb_case_match_node->conditions, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); + + if (erb_case_match_node->else_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->else_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_case_match_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_WHILE_NODE: { + const AST_ERB_WHILE_NODE_T* erb_while_node = (AST_ERB_WHILE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_while_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_while_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_while_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_while_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_while_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_while_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_UNTIL_NODE: { + const AST_ERB_UNTIL_NODE_T* erb_until_node = (AST_ERB_UNTIL_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_until_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_until_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_until_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_until_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_until_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_until_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_FOR_NODE: { + const AST_ERB_FOR_NODE_T* erb_for_node = (AST_ERB_FOR_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_for_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_for_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_for_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_for_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_for_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_for_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_RESCUE_NODE: { + const AST_ERB_RESCUE_NODE_T* erb_rescue_node = (AST_ERB_RESCUE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_rescue_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_rescue_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_rescue_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_rescue_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("subsequent"), indent, relative_indent, true, buffer); + + if (erb_rescue_node->subsequent) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_rescue_node->subsequent, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_ENSURE_NODE: { + const AST_ERB_ENSURE_NODE_T* erb_ensure_node = (AST_ERB_ENSURE_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_ensure_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_ensure_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_ensure_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_ensure_node->statements, indent, relative_indent, true, buffer); + } break; + + case AST_ERB_BEGIN_NODE: { + const AST_ERB_BEGIN_NODE_T* erb_begin_node = (AST_ERB_BEGIN_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_begin_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_begin_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_begin_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_begin_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("rescue_clause"), indent, relative_indent, false, buffer); + + if (erb_begin_node->rescue_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_begin_node->rescue_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); + + if (erb_begin_node->else_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_begin_node->else_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("ensure_clause"), indent, relative_indent, false, buffer); + + if (erb_begin_node->ensure_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_begin_node->ensure_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_begin_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_begin_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_UNLESS_NODE: { + const AST_ERB_UNLESS_NODE_T* erb_unless_node = (AST_ERB_UNLESS_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_unless_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_unless_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_unless_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_unless_node->statements, indent, relative_indent, false, buffer); + + pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); + + if (erb_unless_node->else_clause) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_unless_node->else_clause, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + + pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); + + if (erb_unless_node->end_node) { + hb_buffer_append(buffer, "\n"); + pretty_print_indent(buffer, indent); + pretty_print_indent(buffer, relative_indent + 1); + + hb_buffer_append(buffer, "└── "); + ast_pretty_print_node((AST_NODE_T*) erb_unless_node->end_node, indent, relative_indent + 2, buffer); + } else { + hb_buffer_append(buffer, " ∅\n"); + } + hb_buffer_append(buffer, "\n"); + + } break; + + case AST_ERB_YIELD_NODE: { + const AST_ERB_YIELD_NODE_T* erb_yield_node = (AST_ERB_YIELD_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_yield_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_yield_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_yield_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); + } break; + + case AST_ERB_IN_NODE: { + const AST_ERB_IN_NODE_T* erb_in_node = (AST_ERB_IN_NODE_T*) node; + + pretty_print_errors(node, indent, relative_indent, false, buffer); + pretty_print_token_property(erb_in_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_in_node->content, hb_string("content"), indent, relative_indent, false, buffer); + pretty_print_token_property(erb_in_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); + pretty_print_array(hb_string("statements"), erb_in_node->statements, indent, relative_indent, true, buffer); + } break; + + } +} diff --git a/src/include/ast_nodes.h b/src/include/ast_nodes.h new file mode 100644 index 000000000..e06e8022b --- /dev/null +++ b/src/include/ast_nodes.h @@ -0,0 +1,346 @@ +// NOTE: This file is generated by the templates/template.rb script and should not +// be modified manually. See /Users/dani/oss/herb/templates/src/include/ast_nodes.h.erb + +#ifndef HERB_AST_NODES_H +#define HERB_AST_NODES_H + +#include +#include + +#include "analyzed_ruby.h" +#include "element_source.h" +#include "location.h" +#include "position.h" +#include "token_struct.h" +#include "util/hb_array.h" +#include "util/hb_buffer.h" +#include "util/hb_string.h" + +typedef enum { + AST_DOCUMENT_NODE, + AST_LITERAL_NODE, + AST_HTML_OPEN_TAG_NODE, + AST_HTML_CLOSE_TAG_NODE, + AST_HTML_ELEMENT_NODE, + AST_HTML_ATTRIBUTE_VALUE_NODE, + AST_HTML_ATTRIBUTE_NAME_NODE, + AST_HTML_ATTRIBUTE_NODE, + AST_HTML_TEXT_NODE, + AST_HTML_COMMENT_NODE, + AST_HTML_DOCTYPE_NODE, + AST_XML_DECLARATION_NODE, + AST_CDATA_NODE, + AST_WHITESPACE_NODE, + AST_ERB_CONTENT_NODE, + AST_ERB_END_NODE, + AST_ERB_ELSE_NODE, + AST_ERB_IF_NODE, + AST_ERB_BLOCK_NODE, + AST_ERB_WHEN_NODE, + AST_ERB_CASE_NODE, + AST_ERB_CASE_MATCH_NODE, + AST_ERB_WHILE_NODE, + AST_ERB_UNTIL_NODE, + AST_ERB_FOR_NODE, + AST_ERB_RESCUE_NODE, + AST_ERB_ENSURE_NODE, + AST_ERB_BEGIN_NODE, + AST_ERB_UNLESS_NODE, + AST_ERB_YIELD_NODE, + AST_ERB_IN_NODE, +} ast_node_type_T; + +typedef struct AST_NODE_STRUCT { + ast_node_type_T type; + location_T location; + // maybe a range too? + hb_array_T* errors; +} AST_NODE_T; + + +typedef struct AST_DOCUMENT_NODE_STRUCT { + AST_NODE_T base; + hb_array_T* children; +} AST_DOCUMENT_NODE_T; + +typedef struct AST_LITERAL_NODE_STRUCT { + AST_NODE_T base; + const char* content; +} AST_LITERAL_NODE_T; + +typedef struct AST_HTML_OPEN_TAG_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* tag_name; + token_T* tag_closing; + hb_array_T* children; + bool is_void; +} AST_HTML_OPEN_TAG_NODE_T; + +typedef struct AST_HTML_CLOSE_TAG_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* tag_name; + hb_array_T* children; + token_T* tag_closing; +} AST_HTML_CLOSE_TAG_NODE_T; + +typedef struct AST_HTML_ELEMENT_NODE_STRUCT { + AST_NODE_T base; + struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag; + token_T* tag_name; + hb_array_T* body; + struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag; + bool is_void; + element_source_t source; +} AST_HTML_ELEMENT_NODE_T; + +typedef struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT { + AST_NODE_T base; + token_T* open_quote; + hb_array_T* children; + token_T* close_quote; + bool quoted; +} AST_HTML_ATTRIBUTE_VALUE_NODE_T; + +typedef struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT { + AST_NODE_T base; + hb_array_T* children; +} AST_HTML_ATTRIBUTE_NAME_NODE_T; + +typedef struct AST_HTML_ATTRIBUTE_NODE_STRUCT { + AST_NODE_T base; + struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name; + token_T* equals; + struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value; +} AST_HTML_ATTRIBUTE_NODE_T; + +typedef struct AST_HTML_TEXT_NODE_STRUCT { + AST_NODE_T base; + const char* content; +} AST_HTML_TEXT_NODE_T; + +typedef struct AST_HTML_COMMENT_NODE_STRUCT { + AST_NODE_T base; + token_T* comment_start; + hb_array_T* children; + token_T* comment_end; +} AST_HTML_COMMENT_NODE_T; + +typedef struct AST_HTML_DOCTYPE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + hb_array_T* children; + token_T* tag_closing; +} AST_HTML_DOCTYPE_NODE_T; + +typedef struct AST_XML_DECLARATION_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + hb_array_T* children; + token_T* tag_closing; +} AST_XML_DECLARATION_NODE_T; + +typedef struct AST_CDATA_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + hb_array_T* children; + token_T* tag_closing; +} AST_CDATA_NODE_T; + +typedef struct AST_WHITESPACE_NODE_STRUCT { + AST_NODE_T base; + token_T* value; +} AST_WHITESPACE_NODE_T; + +typedef struct AST_ERB_CONTENT_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + analyzed_ruby_T* analyzed_ruby; + bool parsed; + bool valid; +} AST_ERB_CONTENT_NODE_T; + +typedef struct AST_ERB_END_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; +} AST_ERB_END_NODE_T; + +typedef struct AST_ERB_ELSE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; +} AST_ERB_ELSE_NODE_T; + +typedef struct AST_ERB_IF_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_NODE_STRUCT* subsequent; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_IF_NODE_T; + +typedef struct AST_ERB_BLOCK_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* body; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_BLOCK_NODE_T; + +typedef struct AST_ERB_WHEN_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; +} AST_ERB_WHEN_NODE_T; + +typedef struct AST_ERB_CASE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* children; + hb_array_T* conditions; + struct AST_ERB_ELSE_NODE_STRUCT* else_clause; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_CASE_NODE_T; + +typedef struct AST_ERB_CASE_MATCH_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* children; + hb_array_T* conditions; + struct AST_ERB_ELSE_NODE_STRUCT* else_clause; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_CASE_MATCH_NODE_T; + +typedef struct AST_ERB_WHILE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_WHILE_NODE_T; + +typedef struct AST_ERB_UNTIL_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_UNTIL_NODE_T; + +typedef struct AST_ERB_FOR_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_FOR_NODE_T; + +typedef struct AST_ERB_RESCUE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_RESCUE_NODE_STRUCT* subsequent; +} AST_ERB_RESCUE_NODE_T; + +typedef struct AST_ERB_ENSURE_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; +} AST_ERB_ENSURE_NODE_T; + +typedef struct AST_ERB_BEGIN_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause; + struct AST_ERB_ELSE_NODE_STRUCT* else_clause; + struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_BEGIN_NODE_T; + +typedef struct AST_ERB_UNLESS_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; + struct AST_ERB_ELSE_NODE_STRUCT* else_clause; + struct AST_ERB_END_NODE_STRUCT* end_node; +} AST_ERB_UNLESS_NODE_T; + +typedef struct AST_ERB_YIELD_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; +} AST_ERB_YIELD_NODE_T; + +typedef struct AST_ERB_IN_NODE_STRUCT { + AST_NODE_T base; + token_T* tag_opening; + token_T* content; + token_T* tag_closing; + hb_array_T* statements; +} AST_ERB_IN_NODE_T; + +AST_DOCUMENT_NODE_T* ast_document_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors); +AST_LITERAL_NODE_T* ast_literal_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_OPEN_TAG_NODE_T* ast_html_open_tag_node_init(token_T* tag_opening, token_T* tag_name, token_T* tag_closing, hb_array_T* children, bool is_void, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_CLOSE_TAG_NODE_T* ast_html_close_tag_node_init(token_T* tag_opening, token_T* tag_name, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_ELEMENT_NODE_T* ast_html_element_node_init(struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag, token_T* tag_name, hb_array_T* body, struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag, bool is_void, element_source_t source, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_ATTRIBUTE_VALUE_NODE_T* ast_html_attribute_value_node_init(token_T* open_quote, hb_array_T* children, token_T* close_quote, bool quoted, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_ATTRIBUTE_NAME_NODE_T* ast_html_attribute_name_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_ATTRIBUTE_NODE_T* ast_html_attribute_node_init(struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name, token_T* equals, struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_TEXT_NODE_T* ast_html_text_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_COMMENT_NODE_T* ast_html_comment_node_init(token_T* comment_start, hb_array_T* children, token_T* comment_end, position_T start_position, position_T end_position, hb_array_T* errors); +AST_HTML_DOCTYPE_NODE_T* ast_html_doctype_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_XML_DECLARATION_NODE_T* ast_xml_declaration_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_CDATA_NODE_T* ast_cdata_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_WHITESPACE_NODE_T* ast_whitespace_node_init(token_T* value, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_CONTENT_NODE_T* ast_erb_content_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, analyzed_ruby_T* analyzed_ruby, bool parsed, bool valid, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_END_NODE_T* ast_erb_end_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* body, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_UNTIL_NODE_T* ast_erb_until_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_FOR_NODE_T* ast_erb_for_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_RESCUE_NODE_T* ast_erb_rescue_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* subsequent, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_ENSURE_NODE_T* ast_erb_ensure_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); +AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); + +hb_string_T ast_node_type_to_string(AST_NODE_T* node); +hb_string_T ast_node_human_type(AST_NODE_T* node); + +#endif diff --git a/src/include/ast_pretty_print.h b/src/include/ast_pretty_print.h new file mode 100644 index 000000000..27010224e --- /dev/null +++ b/src/include/ast_pretty_print.h @@ -0,0 +1,17 @@ +// NOTE: This file is generated by the templates/template.rb script and should not +// be modified manually. See /Users/dani/oss/herb/templates/src/include/ast_pretty_print.h.erb + +#ifndef HERB_AST_PRETTY_PRINT_H +#define HERB_AST_PRETTY_PRINT_H + +#include "ast_nodes.h" +#include "util/hb_buffer.h" + +void ast_pretty_print_node( + AST_NODE_T* node, + size_t indent, + size_t relative_indent, + hb_buffer_T* buffer +); + +#endif diff --git a/workspace.json b/workspace.json index e95cd9f0d..27648b191 100644 --- a/workspace.json +++ b/workspace.json @@ -16,7 +16,6 @@ "@herb-tools/rewriter": "javascript/packages/rewriter", "@herb-tools/tailwind-class-sorter": "javascript/packages/tailwind-class-sorter", "stimulus-lint": "javascript/packages/stimulus-lint", - "turbo-lint": "javascript/packages/turbo-lint", "herb-language-server": "javascript/packages/herb-language-server", "vscode": "javascript/packages/vscode", "docs": "./docs", From dfaf5518fc9d10a06d6b6f22ca7022ba16888870 Mon Sep 17 00:00:00 2001 From: Dani Bengl Date: Wed, 6 May 2026 17:07:35 +0200 Subject: [PATCH 14/17] Update snapshot --- .../test/__snapshots__/cli.test.ts.snap | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap b/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap index 31a0a562e..921b77f4f 100644 --- a/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap +++ b/javascript/packages/linter/test/__snapshots__/cli.test.ts.snap @@ -111,7 +111,7 @@ test/fixtures/ignored.html.erb:8:8 Offenses 5 errors | 2 warnings (7 offenses across 1 file) Note 3 additional offenses reported (would have been ignored) Fixable 7 offenses | 4 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > Excluded Files > skips excluded file in subdirectory with README.md project indicator 1`] = ` @@ -177,7 +177,7 @@ test/fixtures/test-file-with-errors.html.erb:2:22 Checked 1 file Offenses 2 errors | 1 warning (3 offenses across 1 file) Fixable 3 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > GitHub Actions format includes rule codes 1`] = ` @@ -202,7 +202,7 @@ test/fixtures/no-trailing-newline.html.erb:1:29 Checked 1 file Offenses 1 error | 0 warnings (1 offense across 1 file) Fixable 1 offense | 1 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > GitHub Actions format includes rule codes 2`] = ` @@ -266,7 +266,7 @@ test/fixtures/erb-no-extra-whitespace-inside-tags.html.erb:1:4 Checked 1 file Offenses 4 errors | 0 warnings (4 offenses across 1 file) Fixable 4 offenses | 3 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > Ignores disabled rules 1`] = ` @@ -307,7 +307,7 @@ test/fixtures/ignored.html.erb:6:14 Checked 1 file Offenses 2 errors | 0 warnings | 3 ignored (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > allows tag.attributes in attribute position 1`] = ` @@ -346,7 +346,7 @@ test/fixtures/tag-attributes.html.erb:5:0 Checked 1 file Offenses 2 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > diplays only parsers errors if one is present 1`] = ` @@ -372,7 +372,7 @@ test/fixtures/parser-errors.html.erb:2:16 Checked 1 file Offenses 1 error | 0 warnings (1 offense across 1 file) Fixable 0 offenses - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > displays most violated rules with multiple offenses 1`] = ` @@ -587,7 +587,7 @@ test/fixtures/multiple-rule-offenses.html.erb:4:2 Checked 1 file Offenses 8 errors | 6 warnings (14 offenses across 1 file) Fixable 14 offenses | 4 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > displays rule offenses when showing all rules 1`] = ` @@ -684,7 +684,7 @@ test/fixtures/few-rule-offenses.html.erb:6:0 Checked 1 file Offenses 4 errors | 2 warnings (6 offenses across 1 file) Fixable 6 offenses | 3 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats GitHub Actions output correctly for bad file 1`] = ` @@ -723,7 +723,7 @@ test/fixtures/bad-file.html.erb:1:16 Checked 1 file Offenses 2 errors | 0 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats GitHub Actions output correctly for clean file 1`] = ` @@ -735,7 +735,7 @@ exports[`CLI Output Formatting > formats GitHub Actions output correctly for cle Checked 1 file Offenses 0 offenses Fixable 0 offenses - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats GitHub Actions output correctly for file with errors 1`] = ` @@ -795,7 +795,7 @@ test/fixtures/test-file-with-errors.html.erb:2:22 Checked 1 file Offenses 2 errors | 1 warning (3 offenses across 1 file) Fixable 3 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats GitHub Actions output with --format=github option 1`] = ` @@ -834,7 +834,7 @@ test/fixtures/test-file-simple.html.erb:2:22 Checked 1 file Offenses 2 errors | 0 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats JSON output correctly for bad file 1`] = ` @@ -1038,7 +1038,7 @@ test/fixtures/test-file-with-errors.html.erb:2:22 Checked 1 file Offenses 2 errors | 1 warning (3 offenses across 1 file) Fixable 3 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats simple output correctly 1`] = ` @@ -1057,7 +1057,7 @@ test/fixtures/test-file-simple.html.erb: Checked 1 file Offenses 2 errors | 0 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats simple output for bad-file correctly 1`] = ` @@ -1076,7 +1076,7 @@ test/fixtures/bad-file.html.erb: Checked 1 file Offenses 2 errors | 0 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > formats success output correctly 1`] = ` @@ -1088,7 +1088,7 @@ exports[`CLI Output Formatting > formats success output correctly 1`] = ` Checked 1 file Offenses 0 offenses Fixable 0 offenses - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > handles boolean attributes 1`] = ` @@ -1100,7 +1100,7 @@ exports[`CLI Output Formatting > handles boolean attributes 1`] = ` Checked 1 file Offenses 0 offenses Fixable 0 offenses - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > handles multiple errors correctly 1`] = ` @@ -1135,7 +1135,7 @@ test/fixtures/bad-file.html.erb:1:16 Checked 1 file Offenses 2 errors | 0 warnings (2 offenses across 1 file) Fixable 2 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > herb:disable rules 1`] = ` @@ -1267,7 +1267,7 @@ test/fixtures/disabled-1.html.erb:14:19 Checked 1 file Offenses 2 errors | 6 warnings | 8 ignored (8 offenses across 1 file) Fixable 8 offenses | 1 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > herb:disable rules 2`] = ` @@ -1470,7 +1470,7 @@ test/fixtures/disabled-2.html.erb:2:44 Checked 1 file Offenses 6 errors | 7 warnings | 5 ignored (13 offenses across 1 file) Fixable 13 offenses | 6 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; exports[`CLI Output Formatting > uses GitHub Actions format by default when GITHUB_ACTIONS is true 1`] = ` @@ -1530,5 +1530,5 @@ test/fixtures/test-file-with-errors.html.erb:2:22 Checked 1 file Offenses 2 errors | 1 warning (3 offenses across 1 file) Fixable 3 offenses | 2 autocorrectable using \`--fix\` - Rules 83 enabled | 10 not enabled" + Rules 84 enabled | 10 not enabled" `; From e73c11d599cf46e7402433ea1212a862c2f756b7 Mon Sep 17 00:00:00 2001 From: Dani Bengl Date: Wed, 6 May 2026 17:42:00 +0200 Subject: [PATCH 15/17] Rename rule to turbo-permanent-no-misleading-value --- javascript/packages/linter/docs/rules/README.md | 2 +- ...ermanent.md => turbo-permanent-no-misleading-value.md} | 4 ++-- javascript/packages/linter/src/rules.ts | 4 ++-- ...ermanent.ts => turbo-permanent-no-misleading-value.ts} | 8 ++++---- ...est.ts => turbo-permanent-no-misleading-value.test.ts} | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) rename javascript/packages/linter/docs/rules/{html-turbo-permanent.md => turbo-permanent-no-misleading-value.md} (89%) rename javascript/packages/linter/src/rules/{html-turbo-permanent.ts => turbo-permanent-no-misleading-value.ts} (80%) rename javascript/packages/linter/test/rules/{html-turbo-permanent.test.ts => turbo-permanent-no-misleading-value.test.ts} (85%) diff --git a/javascript/packages/linter/docs/rules/README.md b/javascript/packages/linter/docs/rules/README.md index a044b45ea..cfd596a4a 100644 --- a/javascript/packages/linter/docs/rules/README.md +++ b/javascript/packages/linter/docs/rules/README.md @@ -126,7 +126,7 @@ This page contains documentation for all Herb Linter rules. #### Turbo -- [`html-turbo-permanent`](./html-turbo-permanent.md) - Disallow values on `data-turbo-permanent` +- [`turbo-permanent-no-misleading-value`](./turbo-permanent-no-misleading-value.md) - Disallow misleading values on `data-turbo-permanent` - [`turbo-permanent-require-id`](./turbo-permanent-require-id.md) - Require `id` attribute on elements with `data-turbo-permanent` diff --git a/javascript/packages/linter/docs/rules/html-turbo-permanent.md b/javascript/packages/linter/docs/rules/turbo-permanent-no-misleading-value.md similarity index 89% rename from javascript/packages/linter/docs/rules/html-turbo-permanent.md rename to javascript/packages/linter/docs/rules/turbo-permanent-no-misleading-value.md index f7bf8942b..b5d64eb3f 100644 --- a/javascript/packages/linter/docs/rules/html-turbo-permanent.md +++ b/javascript/packages/linter/docs/rules/turbo-permanent-no-misleading-value.md @@ -1,6 +1,6 @@ -# Linter Rule: HTML Turbo permanent attribute usage +# Linter Rule: Disallow misleading values on `data-turbo-permanent` -**Rule:** `html-turbo-permanent` +**Rule:** `turbo-permanent-no-misleading-value` ## Description diff --git a/javascript/packages/linter/src/rules.ts b/javascript/packages/linter/src/rules.ts index 91adc8fcb..2fb1e2750 100644 --- a/javascript/packages/linter/src/rules.ts +++ b/javascript/packages/linter/src/rules.ts @@ -93,7 +93,6 @@ import { HTMLNoUnderscoresInAttributeNamesRule } from "./rules/html-no-underscor import { HTMLRequireClosingTagsRule } from "./rules/html-require-closing-tags.js" import { HTMLRequireScriptNonceRule } from "./rules/html-require-script-nonce.js" import { HTMLTagNameLowercaseRule } from "./rules/html-tag-name-lowercase.js" -import { HTMLTurboPermanentRule } from "./rules/html-turbo-permanent.js" import { ParserNoErrorsRule } from "./rules/parser-no-errors.js" @@ -101,6 +100,7 @@ import { SourceIndentationRule } from "./rules/source-indentation.js" import { SVGTagNameCapitalizationRule } from "./rules/svg-tag-name-capitalization.js" +import { TurboPermanentNoMisleadingValueRule } from "./rules/turbo-permanent-no-misleading-value.js" import { TurboPermanentRequireIdRule } from "./rules/turbo-permanent-require-id.js" export const rules: RuleClass[] = [ @@ -197,7 +197,6 @@ export const rules: RuleClass[] = [ HTMLRequireClosingTagsRule, HTMLRequireScriptNonceRule, HTMLTagNameLowercaseRule, - HTMLTurboPermanentRule, ParserNoErrorsRule, @@ -205,5 +204,6 @@ export const rules: RuleClass[] = [ SVGTagNameCapitalizationRule, + TurboPermanentNoMisleadingValueRule, TurboPermanentRequireIdRule, ] diff --git a/javascript/packages/linter/src/rules/html-turbo-permanent.ts b/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts similarity index 80% rename from javascript/packages/linter/src/rules/html-turbo-permanent.ts rename to javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts index 473204b06..1c23946d8 100644 --- a/javascript/packages/linter/src/rules/html-turbo-permanent.ts +++ b/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts @@ -5,7 +5,7 @@ import { ParserRule } from "../types.js" import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js" import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" -class HTMLTurboPermanentVisitor extends BaseRuleVisitor { +class TurboPermanentNoMisleadingValueVisitor extends BaseRuleVisitor { visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { this.checkTurboPermanentAttribute(node) super.visitHTMLOpenTagNode(node) @@ -24,8 +24,8 @@ class HTMLTurboPermanentVisitor extends BaseRuleVisitor { } } -export class HTMLTurboPermanentRule extends ParserRule { - static ruleName = "html-turbo-permanent" +export class TurboPermanentNoMisleadingValueRule extends ParserRule { + static ruleName = "turbo-permanent-no-misleading-value" static introducedIn = this.version("0.9.0") get defaultConfig(): FullRuleConfig { @@ -36,7 +36,7 @@ export class HTMLTurboPermanentRule extends ParserRule { } check(result: ParseResult, context?: Partial): UnboundLintOffense[] { - const visitor = new HTMLTurboPermanentVisitor(this.ruleName, context) + const visitor = new TurboPermanentNoMisleadingValueVisitor(this.ruleName, context) visitor.visit(result.value) diff --git a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts b/javascript/packages/linter/test/rules/turbo-permanent-no-misleading-value.test.ts similarity index 85% rename from javascript/packages/linter/test/rules/html-turbo-permanent.test.ts rename to javascript/packages/linter/test/rules/turbo-permanent-no-misleading-value.test.ts index 91f96ba18..aaec2830e 100644 --- a/javascript/packages/linter/test/rules/html-turbo-permanent.test.ts +++ b/javascript/packages/linter/test/rules/turbo-permanent-no-misleading-value.test.ts @@ -1,12 +1,12 @@ import { describe, test } from "vitest" -import { HTMLTurboPermanentRule } from "../../src/rules/html-turbo-permanent.js" +import { TurboPermanentNoMisleadingValueRule } from "../../src/rules/turbo-permanent-no-misleading-value.js" import { createLinterTest } from "../helpers/linter-test-helper.js" -const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(HTMLTurboPermanentRule) +const { expectNoOffenses, expectError, assertOffenses } = createLinterTest(TurboPermanentNoMisleadingValueRule) const MESSAGE = "Attribute `data-turbo-permanent` should not contain any value. Its presence alone enables the behavior, so values like `\"true\"` or `\"false\"` are misleading." -describe("html-turbo-permanent", () => { +describe("turbo-permanent-no-misleading-value", () => { test("passes when no explicit value is given", () => { expectNoOffenses('
1 item
') }) From 17ca4919885dbe2bfe982466ed85ab6196a0d78f Mon Sep 17 00:00:00 2001 From: Dani Bengl Date: Wed, 6 May 2026 17:45:49 +0200 Subject: [PATCH 16/17] Introduce autofix --- .../turbo-permanent-no-misleading-value.ts | 31 +++++++-- ...manent-no-misleading-value.autofix.test.ts | 65 +++++++++++++++++++ 2 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 javascript/packages/linter/test/autofix/turbo-permanent-no-misleading-value.autofix.test.ts diff --git a/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts b/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts index 1c23946d8..0d52d6a76 100644 --- a/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts +++ b/javascript/packages/linter/src/rules/turbo-permanent-no-misleading-value.ts @@ -1,11 +1,15 @@ import { BaseRuleVisitor } from "./rule-utils.js" import { getAttribute, hasAttributeValue } from "@herb-tools/core" -import { ParserRule } from "../types.js" -import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js" -import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core" +import { ParserRule, BaseAutofixContext, Mutable } from "../types.js" +import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js" +import type { HTMLAttributeNode, HTMLOpenTagNode, ParseResult } from "@herb-tools/core" -class TurboPermanentNoMisleadingValueVisitor extends BaseRuleVisitor { +interface TurboPermanentAutofixContext extends BaseAutofixContext { + node: Mutable +} + +class TurboPermanentNoMisleadingValueVisitor extends BaseRuleVisitor { visitHTMLOpenTagNode(node: HTMLOpenTagNode): void { this.checkTurboPermanentAttribute(node) super.visitHTMLOpenTagNode(node) @@ -20,11 +24,15 @@ class TurboPermanentNoMisleadingValueVisitor extends BaseRuleVisitor { this.addOffense( "Attribute `data-turbo-permanent` should not contain any value. Its presence alone enables the behavior, so values like `\"true\"` or `\"false\"` are misleading.", attribute.value!.location, + { + node: attribute + } ) } } -export class TurboPermanentNoMisleadingValueRule extends ParserRule { +export class TurboPermanentNoMisleadingValueRule extends ParserRule { + static autocorrectable = true static ruleName = "turbo-permanent-no-misleading-value" static introducedIn = this.version("0.9.0") @@ -35,11 +43,22 @@ export class TurboPermanentNoMisleadingValueRule extends ParserRule { } } - check(result: ParseResult, context?: Partial): UnboundLintOffense[] { + check(result: ParseResult, context?: Partial): UnboundLintOffense[] { const visitor = new TurboPermanentNoMisleadingValueVisitor(this.ruleName, context) visitor.visit(result.value) return visitor.offenses } + + autofix(offense: LintOffense, result: ParseResult, _context?: Partial): ParseResult | null { + if (!offense.autofixContext) return null + + const { node } = offense.autofixContext + + node.equals = null + node.value = null + + return result + } } diff --git a/javascript/packages/linter/test/autofix/turbo-permanent-no-misleading-value.autofix.test.ts b/javascript/packages/linter/test/autofix/turbo-permanent-no-misleading-value.autofix.test.ts new file mode 100644 index 000000000..c100daa93 --- /dev/null +++ b/javascript/packages/linter/test/autofix/turbo-permanent-no-misleading-value.autofix.test.ts @@ -0,0 +1,65 @@ +import { describe, test, expect, beforeAll } from "vitest" +import { Herb } from "@herb-tools/node-wasm" +import { Linter } from "../../src/linter.js" +import { TurboPermanentNoMisleadingValueRule } from "../../src/rules/turbo-permanent-no-misleading-value.js" + +describe("turbo-permanent-no-misleading-value autofix", () => { + beforeAll(async () => { + await Herb.load() + }) + + test("removes `\"true\"` value", () => { + const input = '
1 item
' + const expected = '
1 item
' + + const linter = new Linter(Herb, [TurboPermanentNoMisleadingValueRule]) + const result = linter.autofix(input) + + expect(result.source).toBe(expected) + expect(result.fixed).toHaveLength(1) + expect(result.unfixed).toHaveLength(0) + }) + + test("removes `\"false\"` value", () => { + const input = '
1 item
' + const expected = '
1 item
' + + const linter = new Linter(Herb, [TurboPermanentNoMisleadingValueRule]) + const result = linter.autofix(input) + + expect(result.source).toBe(expected) + expect(result.fixed).toHaveLength(1) + }) + + test("removes empty string value", () => { + const input = '
1 item
' + const expected = '
1 item
' + + const linter = new Linter(Herb, [TurboPermanentNoMisleadingValueRule]) + const result = linter.autofix(input) + + expect(result.source).toBe(expected) + expect(result.fixed).toHaveLength(1) + }) + + test("removes arbitrary value", () => { + const input = '
1 item
' + const expected = '
1 item
' + + const linter = new Linter(Herb, [TurboPermanentNoMisleadingValueRule]) + const result = linter.autofix(input) + + expect(result.source).toBe(expected) + expect(result.fixed).toHaveLength(1) + }) + + test("leaves valueless attribute untouched", () => { + const input = '
1 item
' + + const linter = new Linter(Herb, [TurboPermanentNoMisleadingValueRule]) + const result = linter.autofix(input) + + expect(result.source).toBe(input) + expect(result.fixed).toHaveLength(0) + }) +}) From 29efc340e579de49d115c6f208c2e117639da6f8 Mon Sep 17 00:00:00 2001 From: Dani Bengl Date: Wed, 6 May 2026 18:03:49 +0200 Subject: [PATCH 17/17] Simplify diff --- src/ast_nodes.c | 1010 -------------------------------- src/ast_pretty_print.c | 657 --------------------- src/include/ast_nodes.h | 346 ----------- src/include/ast_pretty_print.h | 17 - 4 files changed, 2030 deletions(-) delete mode 100644 src/ast_nodes.c delete mode 100644 src/ast_pretty_print.c delete mode 100644 src/include/ast_nodes.h delete mode 100644 src/include/ast_pretty_print.h diff --git a/src/ast_nodes.c b/src/ast_nodes.c deleted file mode 100644 index 48d6a8ab5..000000000 --- a/src/ast_nodes.c +++ /dev/null @@ -1,1010 +0,0 @@ -// NOTE: This file is generated by the templates/template.rb script and should not -// be modified manually. See /Users/dani/oss/herb/templates/src/ast_nodes.c.erb - -#include -#include - -#include - -#include "include/analyzed_ruby.h" -#include "include/ast_node.h" -#include "include/ast_nodes.h" -#include "include/errors.h" -#include "include/token.h" -#include "include/util.h" -#include "include/util/hb_array.h" - - -AST_DOCUMENT_NODE_T* ast_document_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_DOCUMENT_NODE_T* document_node = malloc(sizeof(AST_DOCUMENT_NODE_T)); - - ast_node_init(&document_node->base, AST_DOCUMENT_NODE, start_position, end_position, errors); - - document_node->children = children; - - return document_node; -} - -AST_LITERAL_NODE_T* ast_literal_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_LITERAL_NODE_T* literal_node = malloc(sizeof(AST_LITERAL_NODE_T)); - - ast_node_init(&literal_node->base, AST_LITERAL_NODE, start_position, end_position, errors); - - literal_node->content = herb_strdup(content); - - return literal_node; -} - -AST_HTML_OPEN_TAG_NODE_T* ast_html_open_tag_node_init(token_T* tag_opening, token_T* tag_name, token_T* tag_closing, hb_array_T* children, bool is_void, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node = malloc(sizeof(AST_HTML_OPEN_TAG_NODE_T)); - - ast_node_init(&html_open_tag_node->base, AST_HTML_OPEN_TAG_NODE, start_position, end_position, errors); - - html_open_tag_node->tag_opening = token_copy(tag_opening); - html_open_tag_node->tag_name = token_copy(tag_name); - html_open_tag_node->tag_closing = token_copy(tag_closing); - html_open_tag_node->children = children; - html_open_tag_node->is_void = is_void; - - return html_open_tag_node; -} - -AST_HTML_CLOSE_TAG_NODE_T* ast_html_close_tag_node_init(token_T* tag_opening, token_T* tag_name, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node = malloc(sizeof(AST_HTML_CLOSE_TAG_NODE_T)); - - ast_node_init(&html_close_tag_node->base, AST_HTML_CLOSE_TAG_NODE, start_position, end_position, errors); - - html_close_tag_node->tag_opening = token_copy(tag_opening); - html_close_tag_node->tag_name = token_copy(tag_name); - html_close_tag_node->children = children; - html_close_tag_node->tag_closing = token_copy(tag_closing); - - return html_close_tag_node; -} - -AST_HTML_ELEMENT_NODE_T* ast_html_element_node_init(struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag, token_T* tag_name, hb_array_T* body, struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag, bool is_void, element_source_t source, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_ELEMENT_NODE_T* html_element_node = malloc(sizeof(AST_HTML_ELEMENT_NODE_T)); - - ast_node_init(&html_element_node->base, AST_HTML_ELEMENT_NODE, start_position, end_position, errors); - - html_element_node->open_tag = open_tag; - html_element_node->tag_name = token_copy(tag_name); - html_element_node->body = body; - html_element_node->close_tag = close_tag; - html_element_node->is_void = is_void; - html_element_node->source = source; - - return html_element_node; -} - -AST_HTML_ATTRIBUTE_VALUE_NODE_T* ast_html_attribute_value_node_init(token_T* open_quote, hb_array_T* children, token_T* close_quote, bool quoted, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node = malloc(sizeof(AST_HTML_ATTRIBUTE_VALUE_NODE_T)); - - ast_node_init(&html_attribute_value_node->base, AST_HTML_ATTRIBUTE_VALUE_NODE, start_position, end_position, errors); - - html_attribute_value_node->open_quote = token_copy(open_quote); - html_attribute_value_node->children = children; - html_attribute_value_node->close_quote = token_copy(close_quote); - html_attribute_value_node->quoted = quoted; - - return html_attribute_value_node; -} - -AST_HTML_ATTRIBUTE_NAME_NODE_T* ast_html_attribute_name_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node = malloc(sizeof(AST_HTML_ATTRIBUTE_NAME_NODE_T)); - - ast_node_init(&html_attribute_name_node->base, AST_HTML_ATTRIBUTE_NAME_NODE, start_position, end_position, errors); - - html_attribute_name_node->children = children; - - return html_attribute_name_node; -} - -AST_HTML_ATTRIBUTE_NODE_T* ast_html_attribute_node_init(struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name, token_T* equals, struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node = malloc(sizeof(AST_HTML_ATTRIBUTE_NODE_T)); - - ast_node_init(&html_attribute_node->base, AST_HTML_ATTRIBUTE_NODE, start_position, end_position, errors); - - html_attribute_node->name = name; - html_attribute_node->equals = token_copy(equals); - html_attribute_node->value = value; - - return html_attribute_node; -} - -AST_HTML_TEXT_NODE_T* ast_html_text_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_TEXT_NODE_T* html_text_node = malloc(sizeof(AST_HTML_TEXT_NODE_T)); - - ast_node_init(&html_text_node->base, AST_HTML_TEXT_NODE, start_position, end_position, errors); - - html_text_node->content = herb_strdup(content); - - return html_text_node; -} - -AST_HTML_COMMENT_NODE_T* ast_html_comment_node_init(token_T* comment_start, hb_array_T* children, token_T* comment_end, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_COMMENT_NODE_T* html_comment_node = malloc(sizeof(AST_HTML_COMMENT_NODE_T)); - - ast_node_init(&html_comment_node->base, AST_HTML_COMMENT_NODE, start_position, end_position, errors); - - html_comment_node->comment_start = token_copy(comment_start); - html_comment_node->children = children; - html_comment_node->comment_end = token_copy(comment_end); - - return html_comment_node; -} - -AST_HTML_DOCTYPE_NODE_T* ast_html_doctype_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_HTML_DOCTYPE_NODE_T* html_doctype_node = malloc(sizeof(AST_HTML_DOCTYPE_NODE_T)); - - ast_node_init(&html_doctype_node->base, AST_HTML_DOCTYPE_NODE, start_position, end_position, errors); - - html_doctype_node->tag_opening = token_copy(tag_opening); - html_doctype_node->children = children; - html_doctype_node->tag_closing = token_copy(tag_closing); - - return html_doctype_node; -} - -AST_XML_DECLARATION_NODE_T* ast_xml_declaration_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_XML_DECLARATION_NODE_T* xml_declaration_node = malloc(sizeof(AST_XML_DECLARATION_NODE_T)); - - ast_node_init(&xml_declaration_node->base, AST_XML_DECLARATION_NODE, start_position, end_position, errors); - - xml_declaration_node->tag_opening = token_copy(tag_opening); - xml_declaration_node->children = children; - xml_declaration_node->tag_closing = token_copy(tag_closing); - - return xml_declaration_node; -} - -AST_CDATA_NODE_T* ast_cdata_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_CDATA_NODE_T* cdata_node = malloc(sizeof(AST_CDATA_NODE_T)); - - ast_node_init(&cdata_node->base, AST_CDATA_NODE, start_position, end_position, errors); - - cdata_node->tag_opening = token_copy(tag_opening); - cdata_node->children = children; - cdata_node->tag_closing = token_copy(tag_closing); - - return cdata_node; -} - -AST_WHITESPACE_NODE_T* ast_whitespace_node_init(token_T* value, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_WHITESPACE_NODE_T* whitespace_node = malloc(sizeof(AST_WHITESPACE_NODE_T)); - - ast_node_init(&whitespace_node->base, AST_WHITESPACE_NODE, start_position, end_position, errors); - - whitespace_node->value = token_copy(value); - - return whitespace_node; -} - -AST_ERB_CONTENT_NODE_T* ast_erb_content_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, analyzed_ruby_T* analyzed_ruby, bool parsed, bool valid, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_CONTENT_NODE_T* erb_content_node = malloc(sizeof(AST_ERB_CONTENT_NODE_T)); - - ast_node_init(&erb_content_node->base, AST_ERB_CONTENT_NODE, start_position, end_position, errors); - - erb_content_node->tag_opening = token_copy(tag_opening); - erb_content_node->content = token_copy(content); - erb_content_node->tag_closing = token_copy(tag_closing); - erb_content_node->analyzed_ruby = analyzed_ruby; - erb_content_node->parsed = parsed; - erb_content_node->valid = valid; - - return erb_content_node; -} - -AST_ERB_END_NODE_T* ast_erb_end_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_END_NODE_T* erb_end_node = malloc(sizeof(AST_ERB_END_NODE_T)); - - ast_node_init(&erb_end_node->base, AST_ERB_END_NODE, start_position, end_position, errors); - - erb_end_node->tag_opening = token_copy(tag_opening); - erb_end_node->content = token_copy(content); - erb_end_node->tag_closing = token_copy(tag_closing); - - return erb_end_node; -} - -AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_ELSE_NODE_T* erb_else_node = malloc(sizeof(AST_ERB_ELSE_NODE_T)); - - ast_node_init(&erb_else_node->base, AST_ERB_ELSE_NODE, start_position, end_position, errors); - - erb_else_node->tag_opening = token_copy(tag_opening); - erb_else_node->content = token_copy(content); - erb_else_node->tag_closing = token_copy(tag_closing); - erb_else_node->statements = statements; - - return erb_else_node; -} - -AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_IF_NODE_T* erb_if_node = malloc(sizeof(AST_ERB_IF_NODE_T)); - - ast_node_init(&erb_if_node->base, AST_ERB_IF_NODE, start_position, end_position, errors); - - erb_if_node->tag_opening = token_copy(tag_opening); - erb_if_node->content = token_copy(content); - erb_if_node->tag_closing = token_copy(tag_closing); - erb_if_node->statements = statements; - erb_if_node->subsequent = subsequent; - erb_if_node->end_node = end_node; - - return erb_if_node; -} - -AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* body, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_BLOCK_NODE_T* erb_block_node = malloc(sizeof(AST_ERB_BLOCK_NODE_T)); - - ast_node_init(&erb_block_node->base, AST_ERB_BLOCK_NODE, start_position, end_position, errors); - - erb_block_node->tag_opening = token_copy(tag_opening); - erb_block_node->content = token_copy(content); - erb_block_node->tag_closing = token_copy(tag_closing); - erb_block_node->body = body; - erb_block_node->end_node = end_node; - - return erb_block_node; -} - -AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_WHEN_NODE_T* erb_when_node = malloc(sizeof(AST_ERB_WHEN_NODE_T)); - - ast_node_init(&erb_when_node->base, AST_ERB_WHEN_NODE, start_position, end_position, errors); - - erb_when_node->tag_opening = token_copy(tag_opening); - erb_when_node->content = token_copy(content); - erb_when_node->tag_closing = token_copy(tag_closing); - erb_when_node->statements = statements; - - return erb_when_node; -} - -AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_CASE_NODE_T* erb_case_node = malloc(sizeof(AST_ERB_CASE_NODE_T)); - - ast_node_init(&erb_case_node->base, AST_ERB_CASE_NODE, start_position, end_position, errors); - - erb_case_node->tag_opening = token_copy(tag_opening); - erb_case_node->content = token_copy(content); - erb_case_node->tag_closing = token_copy(tag_closing); - erb_case_node->children = children; - erb_case_node->conditions = conditions; - erb_case_node->else_clause = else_clause; - erb_case_node->end_node = end_node; - - return erb_case_node; -} - -AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = malloc(sizeof(AST_ERB_CASE_MATCH_NODE_T)); - - ast_node_init(&erb_case_match_node->base, AST_ERB_CASE_MATCH_NODE, start_position, end_position, errors); - - erb_case_match_node->tag_opening = token_copy(tag_opening); - erb_case_match_node->content = token_copy(content); - erb_case_match_node->tag_closing = token_copy(tag_closing); - erb_case_match_node->children = children; - erb_case_match_node->conditions = conditions; - erb_case_match_node->else_clause = else_clause; - erb_case_match_node->end_node = end_node; - - return erb_case_match_node; -} - -AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_WHILE_NODE_T* erb_while_node = malloc(sizeof(AST_ERB_WHILE_NODE_T)); - - ast_node_init(&erb_while_node->base, AST_ERB_WHILE_NODE, start_position, end_position, errors); - - erb_while_node->tag_opening = token_copy(tag_opening); - erb_while_node->content = token_copy(content); - erb_while_node->tag_closing = token_copy(tag_closing); - erb_while_node->statements = statements; - erb_while_node->end_node = end_node; - - return erb_while_node; -} - -AST_ERB_UNTIL_NODE_T* ast_erb_until_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_UNTIL_NODE_T* erb_until_node = malloc(sizeof(AST_ERB_UNTIL_NODE_T)); - - ast_node_init(&erb_until_node->base, AST_ERB_UNTIL_NODE, start_position, end_position, errors); - - erb_until_node->tag_opening = token_copy(tag_opening); - erb_until_node->content = token_copy(content); - erb_until_node->tag_closing = token_copy(tag_closing); - erb_until_node->statements = statements; - erb_until_node->end_node = end_node; - - return erb_until_node; -} - -AST_ERB_FOR_NODE_T* ast_erb_for_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_FOR_NODE_T* erb_for_node = malloc(sizeof(AST_ERB_FOR_NODE_T)); - - ast_node_init(&erb_for_node->base, AST_ERB_FOR_NODE, start_position, end_position, errors); - - erb_for_node->tag_opening = token_copy(tag_opening); - erb_for_node->content = token_copy(content); - erb_for_node->tag_closing = token_copy(tag_closing); - erb_for_node->statements = statements; - erb_for_node->end_node = end_node; - - return erb_for_node; -} - -AST_ERB_RESCUE_NODE_T* ast_erb_rescue_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* subsequent, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_RESCUE_NODE_T* erb_rescue_node = malloc(sizeof(AST_ERB_RESCUE_NODE_T)); - - ast_node_init(&erb_rescue_node->base, AST_ERB_RESCUE_NODE, start_position, end_position, errors); - - erb_rescue_node->tag_opening = token_copy(tag_opening); - erb_rescue_node->content = token_copy(content); - erb_rescue_node->tag_closing = token_copy(tag_closing); - erb_rescue_node->statements = statements; - erb_rescue_node->subsequent = subsequent; - - return erb_rescue_node; -} - -AST_ERB_ENSURE_NODE_T* ast_erb_ensure_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_ENSURE_NODE_T* erb_ensure_node = malloc(sizeof(AST_ERB_ENSURE_NODE_T)); - - ast_node_init(&erb_ensure_node->base, AST_ERB_ENSURE_NODE, start_position, end_position, errors); - - erb_ensure_node->tag_opening = token_copy(tag_opening); - erb_ensure_node->content = token_copy(content); - erb_ensure_node->tag_closing = token_copy(tag_closing); - erb_ensure_node->statements = statements; - - return erb_ensure_node; -} - -AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_BEGIN_NODE_T* erb_begin_node = malloc(sizeof(AST_ERB_BEGIN_NODE_T)); - - ast_node_init(&erb_begin_node->base, AST_ERB_BEGIN_NODE, start_position, end_position, errors); - - erb_begin_node->tag_opening = token_copy(tag_opening); - erb_begin_node->content = token_copy(content); - erb_begin_node->tag_closing = token_copy(tag_closing); - erb_begin_node->statements = statements; - erb_begin_node->rescue_clause = rescue_clause; - erb_begin_node->else_clause = else_clause; - erb_begin_node->ensure_clause = ensure_clause; - erb_begin_node->end_node = end_node; - - return erb_begin_node; -} - -AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_UNLESS_NODE_T* erb_unless_node = malloc(sizeof(AST_ERB_UNLESS_NODE_T)); - - ast_node_init(&erb_unless_node->base, AST_ERB_UNLESS_NODE, start_position, end_position, errors); - - erb_unless_node->tag_opening = token_copy(tag_opening); - erb_unless_node->content = token_copy(content); - erb_unless_node->tag_closing = token_copy(tag_closing); - erb_unless_node->statements = statements; - erb_unless_node->else_clause = else_clause; - erb_unless_node->end_node = end_node; - - return erb_unless_node; -} - -AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_YIELD_NODE_T* erb_yield_node = malloc(sizeof(AST_ERB_YIELD_NODE_T)); - - ast_node_init(&erb_yield_node->base, AST_ERB_YIELD_NODE, start_position, end_position, errors); - - erb_yield_node->tag_opening = token_copy(tag_opening); - erb_yield_node->content = token_copy(content); - erb_yield_node->tag_closing = token_copy(tag_closing); - - return erb_yield_node; -} - -AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) { - AST_ERB_IN_NODE_T* erb_in_node = malloc(sizeof(AST_ERB_IN_NODE_T)); - - ast_node_init(&erb_in_node->base, AST_ERB_IN_NODE, start_position, end_position, errors); - - erb_in_node->tag_opening = token_copy(tag_opening); - erb_in_node->content = token_copy(content); - erb_in_node->tag_closing = token_copy(tag_closing); - erb_in_node->statements = statements; - - return erb_in_node; -} - -hb_string_T ast_node_type_to_string(AST_NODE_T* node) { - switch (node->type) { - case AST_DOCUMENT_NODE: return hb_string("AST_DOCUMENT_NODE"); - case AST_LITERAL_NODE: return hb_string("AST_LITERAL_NODE"); - case AST_HTML_OPEN_TAG_NODE: return hb_string("AST_HTML_OPEN_TAG_NODE"); - case AST_HTML_CLOSE_TAG_NODE: return hb_string("AST_HTML_CLOSE_TAG_NODE"); - case AST_HTML_ELEMENT_NODE: return hb_string("AST_HTML_ELEMENT_NODE"); - case AST_HTML_ATTRIBUTE_VALUE_NODE: return hb_string("AST_HTML_ATTRIBUTE_VALUE_NODE"); - case AST_HTML_ATTRIBUTE_NAME_NODE: return hb_string("AST_HTML_ATTRIBUTE_NAME_NODE"); - case AST_HTML_ATTRIBUTE_NODE: return hb_string("AST_HTML_ATTRIBUTE_NODE"); - case AST_HTML_TEXT_NODE: return hb_string("AST_HTML_TEXT_NODE"); - case AST_HTML_COMMENT_NODE: return hb_string("AST_HTML_COMMENT_NODE"); - case AST_HTML_DOCTYPE_NODE: return hb_string("AST_HTML_DOCTYPE_NODE"); - case AST_XML_DECLARATION_NODE: return hb_string("AST_XML_DECLARATION_NODE"); - case AST_CDATA_NODE: return hb_string("AST_CDATA_NODE"); - case AST_WHITESPACE_NODE: return hb_string("AST_WHITESPACE_NODE"); - case AST_ERB_CONTENT_NODE: return hb_string("AST_ERB_CONTENT_NODE"); - case AST_ERB_END_NODE: return hb_string("AST_ERB_END_NODE"); - case AST_ERB_ELSE_NODE: return hb_string("AST_ERB_ELSE_NODE"); - case AST_ERB_IF_NODE: return hb_string("AST_ERB_IF_NODE"); - case AST_ERB_BLOCK_NODE: return hb_string("AST_ERB_BLOCK_NODE"); - case AST_ERB_WHEN_NODE: return hb_string("AST_ERB_WHEN_NODE"); - case AST_ERB_CASE_NODE: return hb_string("AST_ERB_CASE_NODE"); - case AST_ERB_CASE_MATCH_NODE: return hb_string("AST_ERB_CASE_MATCH_NODE"); - case AST_ERB_WHILE_NODE: return hb_string("AST_ERB_WHILE_NODE"); - case AST_ERB_UNTIL_NODE: return hb_string("AST_ERB_UNTIL_NODE"); - case AST_ERB_FOR_NODE: return hb_string("AST_ERB_FOR_NODE"); - case AST_ERB_RESCUE_NODE: return hb_string("AST_ERB_RESCUE_NODE"); - case AST_ERB_ENSURE_NODE: return hb_string("AST_ERB_ENSURE_NODE"); - case AST_ERB_BEGIN_NODE: return hb_string("AST_ERB_BEGIN_NODE"); - case AST_ERB_UNLESS_NODE: return hb_string("AST_ERB_UNLESS_NODE"); - case AST_ERB_YIELD_NODE: return hb_string("AST_ERB_YIELD_NODE"); - case AST_ERB_IN_NODE: return hb_string("AST_ERB_IN_NODE"); - } - - return hb_string("Unknown ast_node_type_T"); -} - -hb_string_T ast_node_human_type(AST_NODE_T* node) { - switch (node->type) { - case AST_DOCUMENT_NODE: return hb_string("DocumentNode"); - case AST_LITERAL_NODE: return hb_string("LiteralNode"); - case AST_HTML_OPEN_TAG_NODE: return hb_string("HTMLOpenTagNode"); - case AST_HTML_CLOSE_TAG_NODE: return hb_string("HTMLCloseTagNode"); - case AST_HTML_ELEMENT_NODE: return hb_string("HTMLElementNode"); - case AST_HTML_ATTRIBUTE_VALUE_NODE: return hb_string("HTMLAttributeValueNode"); - case AST_HTML_ATTRIBUTE_NAME_NODE: return hb_string("HTMLAttributeNameNode"); - case AST_HTML_ATTRIBUTE_NODE: return hb_string("HTMLAttributeNode"); - case AST_HTML_TEXT_NODE: return hb_string("HTMLTextNode"); - case AST_HTML_COMMENT_NODE: return hb_string("HTMLCommentNode"); - case AST_HTML_DOCTYPE_NODE: return hb_string("HTMLDoctypeNode"); - case AST_XML_DECLARATION_NODE: return hb_string("XMLDeclarationNode"); - case AST_CDATA_NODE: return hb_string("CDATANode"); - case AST_WHITESPACE_NODE: return hb_string("WhitespaceNode"); - case AST_ERB_CONTENT_NODE: return hb_string("ERBContentNode"); - case AST_ERB_END_NODE: return hb_string("ERBEndNode"); - case AST_ERB_ELSE_NODE: return hb_string("ERBElseNode"); - case AST_ERB_IF_NODE: return hb_string("ERBIfNode"); - case AST_ERB_BLOCK_NODE: return hb_string("ERBBlockNode"); - case AST_ERB_WHEN_NODE: return hb_string("ERBWhenNode"); - case AST_ERB_CASE_NODE: return hb_string("ERBCaseNode"); - case AST_ERB_CASE_MATCH_NODE: return hb_string("ERBCaseMatchNode"); - case AST_ERB_WHILE_NODE: return hb_string("ERBWhileNode"); - case AST_ERB_UNTIL_NODE: return hb_string("ERBUntilNode"); - case AST_ERB_FOR_NODE: return hb_string("ERBForNode"); - case AST_ERB_RESCUE_NODE: return hb_string("ERBRescueNode"); - case AST_ERB_ENSURE_NODE: return hb_string("ERBEnsureNode"); - case AST_ERB_BEGIN_NODE: return hb_string("ERBBeginNode"); - case AST_ERB_UNLESS_NODE: return hb_string("ERBUnlessNode"); - case AST_ERB_YIELD_NODE: return hb_string("ERBYieldNode"); - case AST_ERB_IN_NODE: return hb_string("ERBInNode"); - } - - return hb_string("Unknown ast_node_type_T"); -} - -void ast_free_base_node(AST_NODE_T* node) { - if (node == NULL) { return; } - - if (node->errors) { - for (size_t i = 0; i < hb_array_size(node->errors); i++) { - ERROR_T* child = hb_array_get(node->errors, i); - if (child != NULL) { error_free(child); } - } - - hb_array_free(&node->errors); - } - - free(node); -} - - -static void ast_free_document_node(AST_DOCUMENT_NODE_T* document_node) { - if (document_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(document_node->children); i++) { - AST_NODE_T* child = hb_array_get(document_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&document_node->children); - } - - ast_free_base_node(&document_node->base); -} - -static void ast_free_literal_node(AST_LITERAL_NODE_T* literal_node) { - if (literal_node->content != NULL) { free((char*) literal_node->content); } - - ast_free_base_node(&literal_node->base); -} - -static void ast_free_html_open_tag_node(AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node) { - if (html_open_tag_node->tag_opening != NULL) { token_free(html_open_tag_node->tag_opening); } - if (html_open_tag_node->tag_name != NULL) { token_free(html_open_tag_node->tag_name); } - if (html_open_tag_node->tag_closing != NULL) { token_free(html_open_tag_node->tag_closing); } - if (html_open_tag_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_open_tag_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_open_tag_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_open_tag_node->children); - } - - ast_free_base_node(&html_open_tag_node->base); -} - -static void ast_free_html_close_tag_node(AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node) { - if (html_close_tag_node->tag_opening != NULL) { token_free(html_close_tag_node->tag_opening); } - if (html_close_tag_node->tag_name != NULL) { token_free(html_close_tag_node->tag_name); } - if (html_close_tag_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_close_tag_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_close_tag_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_close_tag_node->children); - } - if (html_close_tag_node->tag_closing != NULL) { token_free(html_close_tag_node->tag_closing); } - - ast_free_base_node(&html_close_tag_node->base); -} - -static void ast_free_html_element_node(AST_HTML_ELEMENT_NODE_T* html_element_node) { - ast_node_free((AST_NODE_T*) html_element_node->open_tag); - if (html_element_node->tag_name != NULL) { token_free(html_element_node->tag_name); } - if (html_element_node->body != NULL) { - for (size_t i = 0; i < hb_array_size(html_element_node->body); i++) { - AST_NODE_T* child = hb_array_get(html_element_node->body, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_element_node->body); - } - ast_node_free((AST_NODE_T*) html_element_node->close_tag); - - ast_free_base_node(&html_element_node->base); -} - -static void ast_free_html_attribute_value_node(AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node) { - if (html_attribute_value_node->open_quote != NULL) { token_free(html_attribute_value_node->open_quote); } - if (html_attribute_value_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_attribute_value_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_attribute_value_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_attribute_value_node->children); - } - if (html_attribute_value_node->close_quote != NULL) { token_free(html_attribute_value_node->close_quote); } - - ast_free_base_node(&html_attribute_value_node->base); -} - -static void ast_free_html_attribute_name_node(AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node) { - if (html_attribute_name_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_attribute_name_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_attribute_name_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_attribute_name_node->children); - } - - ast_free_base_node(&html_attribute_name_node->base); -} - -static void ast_free_html_attribute_node(AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node) { - ast_node_free((AST_NODE_T*) html_attribute_node->name); - if (html_attribute_node->equals != NULL) { token_free(html_attribute_node->equals); } - ast_node_free((AST_NODE_T*) html_attribute_node->value); - - ast_free_base_node(&html_attribute_node->base); -} - -static void ast_free_html_text_node(AST_HTML_TEXT_NODE_T* html_text_node) { - if (html_text_node->content != NULL) { free((char*) html_text_node->content); } - - ast_free_base_node(&html_text_node->base); -} - -static void ast_free_html_comment_node(AST_HTML_COMMENT_NODE_T* html_comment_node) { - if (html_comment_node->comment_start != NULL) { token_free(html_comment_node->comment_start); } - if (html_comment_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_comment_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_comment_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_comment_node->children); - } - if (html_comment_node->comment_end != NULL) { token_free(html_comment_node->comment_end); } - - ast_free_base_node(&html_comment_node->base); -} - -static void ast_free_html_doctype_node(AST_HTML_DOCTYPE_NODE_T* html_doctype_node) { - if (html_doctype_node->tag_opening != NULL) { token_free(html_doctype_node->tag_opening); } - if (html_doctype_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(html_doctype_node->children); i++) { - AST_NODE_T* child = hb_array_get(html_doctype_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&html_doctype_node->children); - } - if (html_doctype_node->tag_closing != NULL) { token_free(html_doctype_node->tag_closing); } - - ast_free_base_node(&html_doctype_node->base); -} - -static void ast_free_xml_declaration_node(AST_XML_DECLARATION_NODE_T* xml_declaration_node) { - if (xml_declaration_node->tag_opening != NULL) { token_free(xml_declaration_node->tag_opening); } - if (xml_declaration_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(xml_declaration_node->children); i++) { - AST_NODE_T* child = hb_array_get(xml_declaration_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&xml_declaration_node->children); - } - if (xml_declaration_node->tag_closing != NULL) { token_free(xml_declaration_node->tag_closing); } - - ast_free_base_node(&xml_declaration_node->base); -} - -static void ast_free_cdata_node(AST_CDATA_NODE_T* cdata_node) { - if (cdata_node->tag_opening != NULL) { token_free(cdata_node->tag_opening); } - if (cdata_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(cdata_node->children); i++) { - AST_NODE_T* child = hb_array_get(cdata_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&cdata_node->children); - } - if (cdata_node->tag_closing != NULL) { token_free(cdata_node->tag_closing); } - - ast_free_base_node(&cdata_node->base); -} - -static void ast_free_whitespace_node(AST_WHITESPACE_NODE_T* whitespace_node) { - if (whitespace_node->value != NULL) { token_free(whitespace_node->value); } - - ast_free_base_node(&whitespace_node->base); -} - -static void ast_free_erb_content_node(AST_ERB_CONTENT_NODE_T* erb_content_node) { - if (erb_content_node->tag_opening != NULL) { token_free(erb_content_node->tag_opening); } - if (erb_content_node->content != NULL) { token_free(erb_content_node->content); } - if (erb_content_node->tag_closing != NULL) { token_free(erb_content_node->tag_closing); } - if (erb_content_node->analyzed_ruby != NULL) { - free_analyzed_ruby(erb_content_node->analyzed_ruby); - } - - ast_free_base_node(&erb_content_node->base); -} - -static void ast_free_erb_end_node(AST_ERB_END_NODE_T* erb_end_node) { - if (erb_end_node->tag_opening != NULL) { token_free(erb_end_node->tag_opening); } - if (erb_end_node->content != NULL) { token_free(erb_end_node->content); } - if (erb_end_node->tag_closing != NULL) { token_free(erb_end_node->tag_closing); } - - ast_free_base_node(&erb_end_node->base); -} - -static void ast_free_erb_else_node(AST_ERB_ELSE_NODE_T* erb_else_node) { - if (erb_else_node->tag_opening != NULL) { token_free(erb_else_node->tag_opening); } - if (erb_else_node->content != NULL) { token_free(erb_else_node->content); } - if (erb_else_node->tag_closing != NULL) { token_free(erb_else_node->tag_closing); } - if (erb_else_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_else_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_else_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_else_node->statements); - } - - ast_free_base_node(&erb_else_node->base); -} - -static void ast_free_erb_if_node(AST_ERB_IF_NODE_T* erb_if_node) { - if (erb_if_node->tag_opening != NULL) { token_free(erb_if_node->tag_opening); } - if (erb_if_node->content != NULL) { token_free(erb_if_node->content); } - if (erb_if_node->tag_closing != NULL) { token_free(erb_if_node->tag_closing); } - if (erb_if_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_if_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_if_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_if_node->statements); - } - ast_node_free((AST_NODE_T*) erb_if_node->subsequent); - ast_node_free((AST_NODE_T*) erb_if_node->end_node); - - ast_free_base_node(&erb_if_node->base); -} - -static void ast_free_erb_block_node(AST_ERB_BLOCK_NODE_T* erb_block_node) { - if (erb_block_node->tag_opening != NULL) { token_free(erb_block_node->tag_opening); } - if (erb_block_node->content != NULL) { token_free(erb_block_node->content); } - if (erb_block_node->tag_closing != NULL) { token_free(erb_block_node->tag_closing); } - if (erb_block_node->body != NULL) { - for (size_t i = 0; i < hb_array_size(erb_block_node->body); i++) { - AST_NODE_T* child = hb_array_get(erb_block_node->body, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_block_node->body); - } - ast_node_free((AST_NODE_T*) erb_block_node->end_node); - - ast_free_base_node(&erb_block_node->base); -} - -static void ast_free_erb_when_node(AST_ERB_WHEN_NODE_T* erb_when_node) { - if (erb_when_node->tag_opening != NULL) { token_free(erb_when_node->tag_opening); } - if (erb_when_node->content != NULL) { token_free(erb_when_node->content); } - if (erb_when_node->tag_closing != NULL) { token_free(erb_when_node->tag_closing); } - if (erb_when_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_when_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_when_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_when_node->statements); - } - - ast_free_base_node(&erb_when_node->base); -} - -static void ast_free_erb_case_node(AST_ERB_CASE_NODE_T* erb_case_node) { - if (erb_case_node->tag_opening != NULL) { token_free(erb_case_node->tag_opening); } - if (erb_case_node->content != NULL) { token_free(erb_case_node->content); } - if (erb_case_node->tag_closing != NULL) { token_free(erb_case_node->tag_closing); } - if (erb_case_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(erb_case_node->children); i++) { - AST_NODE_T* child = hb_array_get(erb_case_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_case_node->children); - } - if (erb_case_node->conditions != NULL) { - for (size_t i = 0; i < hb_array_size(erb_case_node->conditions); i++) { - AST_NODE_T* child = hb_array_get(erb_case_node->conditions, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_case_node->conditions); - } - ast_node_free((AST_NODE_T*) erb_case_node->else_clause); - ast_node_free((AST_NODE_T*) erb_case_node->end_node); - - ast_free_base_node(&erb_case_node->base); -} - -static void ast_free_erb_case_match_node(AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node) { - if (erb_case_match_node->tag_opening != NULL) { token_free(erb_case_match_node->tag_opening); } - if (erb_case_match_node->content != NULL) { token_free(erb_case_match_node->content); } - if (erb_case_match_node->tag_closing != NULL) { token_free(erb_case_match_node->tag_closing); } - if (erb_case_match_node->children != NULL) { - for (size_t i = 0; i < hb_array_size(erb_case_match_node->children); i++) { - AST_NODE_T* child = hb_array_get(erb_case_match_node->children, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_case_match_node->children); - } - if (erb_case_match_node->conditions != NULL) { - for (size_t i = 0; i < hb_array_size(erb_case_match_node->conditions); i++) { - AST_NODE_T* child = hb_array_get(erb_case_match_node->conditions, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_case_match_node->conditions); - } - ast_node_free((AST_NODE_T*) erb_case_match_node->else_clause); - ast_node_free((AST_NODE_T*) erb_case_match_node->end_node); - - ast_free_base_node(&erb_case_match_node->base); -} - -static void ast_free_erb_while_node(AST_ERB_WHILE_NODE_T* erb_while_node) { - if (erb_while_node->tag_opening != NULL) { token_free(erb_while_node->tag_opening); } - if (erb_while_node->content != NULL) { token_free(erb_while_node->content); } - if (erb_while_node->tag_closing != NULL) { token_free(erb_while_node->tag_closing); } - if (erb_while_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_while_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_while_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_while_node->statements); - } - ast_node_free((AST_NODE_T*) erb_while_node->end_node); - - ast_free_base_node(&erb_while_node->base); -} - -static void ast_free_erb_until_node(AST_ERB_UNTIL_NODE_T* erb_until_node) { - if (erb_until_node->tag_opening != NULL) { token_free(erb_until_node->tag_opening); } - if (erb_until_node->content != NULL) { token_free(erb_until_node->content); } - if (erb_until_node->tag_closing != NULL) { token_free(erb_until_node->tag_closing); } - if (erb_until_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_until_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_until_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_until_node->statements); - } - ast_node_free((AST_NODE_T*) erb_until_node->end_node); - - ast_free_base_node(&erb_until_node->base); -} - -static void ast_free_erb_for_node(AST_ERB_FOR_NODE_T* erb_for_node) { - if (erb_for_node->tag_opening != NULL) { token_free(erb_for_node->tag_opening); } - if (erb_for_node->content != NULL) { token_free(erb_for_node->content); } - if (erb_for_node->tag_closing != NULL) { token_free(erb_for_node->tag_closing); } - if (erb_for_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_for_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_for_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_for_node->statements); - } - ast_node_free((AST_NODE_T*) erb_for_node->end_node); - - ast_free_base_node(&erb_for_node->base); -} - -static void ast_free_erb_rescue_node(AST_ERB_RESCUE_NODE_T* erb_rescue_node) { - if (erb_rescue_node->tag_opening != NULL) { token_free(erb_rescue_node->tag_opening); } - if (erb_rescue_node->content != NULL) { token_free(erb_rescue_node->content); } - if (erb_rescue_node->tag_closing != NULL) { token_free(erb_rescue_node->tag_closing); } - if (erb_rescue_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_rescue_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_rescue_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_rescue_node->statements); - } - ast_node_free((AST_NODE_T*) erb_rescue_node->subsequent); - - ast_free_base_node(&erb_rescue_node->base); -} - -static void ast_free_erb_ensure_node(AST_ERB_ENSURE_NODE_T* erb_ensure_node) { - if (erb_ensure_node->tag_opening != NULL) { token_free(erb_ensure_node->tag_opening); } - if (erb_ensure_node->content != NULL) { token_free(erb_ensure_node->content); } - if (erb_ensure_node->tag_closing != NULL) { token_free(erb_ensure_node->tag_closing); } - if (erb_ensure_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_ensure_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_ensure_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_ensure_node->statements); - } - - ast_free_base_node(&erb_ensure_node->base); -} - -static void ast_free_erb_begin_node(AST_ERB_BEGIN_NODE_T* erb_begin_node) { - if (erb_begin_node->tag_opening != NULL) { token_free(erb_begin_node->tag_opening); } - if (erb_begin_node->content != NULL) { token_free(erb_begin_node->content); } - if (erb_begin_node->tag_closing != NULL) { token_free(erb_begin_node->tag_closing); } - if (erb_begin_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_begin_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_begin_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_begin_node->statements); - } - ast_node_free((AST_NODE_T*) erb_begin_node->rescue_clause); - ast_node_free((AST_NODE_T*) erb_begin_node->else_clause); - ast_node_free((AST_NODE_T*) erb_begin_node->ensure_clause); - ast_node_free((AST_NODE_T*) erb_begin_node->end_node); - - ast_free_base_node(&erb_begin_node->base); -} - -static void ast_free_erb_unless_node(AST_ERB_UNLESS_NODE_T* erb_unless_node) { - if (erb_unless_node->tag_opening != NULL) { token_free(erb_unless_node->tag_opening); } - if (erb_unless_node->content != NULL) { token_free(erb_unless_node->content); } - if (erb_unless_node->tag_closing != NULL) { token_free(erb_unless_node->tag_closing); } - if (erb_unless_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_unless_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_unless_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_unless_node->statements); - } - ast_node_free((AST_NODE_T*) erb_unless_node->else_clause); - ast_node_free((AST_NODE_T*) erb_unless_node->end_node); - - ast_free_base_node(&erb_unless_node->base); -} - -static void ast_free_erb_yield_node(AST_ERB_YIELD_NODE_T* erb_yield_node) { - if (erb_yield_node->tag_opening != NULL) { token_free(erb_yield_node->tag_opening); } - if (erb_yield_node->content != NULL) { token_free(erb_yield_node->content); } - if (erb_yield_node->tag_closing != NULL) { token_free(erb_yield_node->tag_closing); } - - ast_free_base_node(&erb_yield_node->base); -} - -static void ast_free_erb_in_node(AST_ERB_IN_NODE_T* erb_in_node) { - if (erb_in_node->tag_opening != NULL) { token_free(erb_in_node->tag_opening); } - if (erb_in_node->content != NULL) { token_free(erb_in_node->content); } - if (erb_in_node->tag_closing != NULL) { token_free(erb_in_node->tag_closing); } - if (erb_in_node->statements != NULL) { - for (size_t i = 0; i < hb_array_size(erb_in_node->statements); i++) { - AST_NODE_T* child = hb_array_get(erb_in_node->statements, i); - if (child) { ast_node_free(child); } - } - - hb_array_free(&erb_in_node->statements); - } - - ast_free_base_node(&erb_in_node->base); -} - -void ast_node_free(AST_NODE_T* node) { - if (!node) { return; } - - switch (node->type) { - case AST_DOCUMENT_NODE: ast_free_document_node((AST_DOCUMENT_NODE_T*) node); break; - case AST_LITERAL_NODE: ast_free_literal_node((AST_LITERAL_NODE_T*) node); break; - case AST_HTML_OPEN_TAG_NODE: ast_free_html_open_tag_node((AST_HTML_OPEN_TAG_NODE_T*) node); break; - case AST_HTML_CLOSE_TAG_NODE: ast_free_html_close_tag_node((AST_HTML_CLOSE_TAG_NODE_T*) node); break; - case AST_HTML_ELEMENT_NODE: ast_free_html_element_node((AST_HTML_ELEMENT_NODE_T*) node); break; - case AST_HTML_ATTRIBUTE_VALUE_NODE: ast_free_html_attribute_value_node((AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node); break; - case AST_HTML_ATTRIBUTE_NAME_NODE: ast_free_html_attribute_name_node((AST_HTML_ATTRIBUTE_NAME_NODE_T*) node); break; - case AST_HTML_ATTRIBUTE_NODE: ast_free_html_attribute_node((AST_HTML_ATTRIBUTE_NODE_T*) node); break; - case AST_HTML_TEXT_NODE: ast_free_html_text_node((AST_HTML_TEXT_NODE_T*) node); break; - case AST_HTML_COMMENT_NODE: ast_free_html_comment_node((AST_HTML_COMMENT_NODE_T*) node); break; - case AST_HTML_DOCTYPE_NODE: ast_free_html_doctype_node((AST_HTML_DOCTYPE_NODE_T*) node); break; - case AST_XML_DECLARATION_NODE: ast_free_xml_declaration_node((AST_XML_DECLARATION_NODE_T*) node); break; - case AST_CDATA_NODE: ast_free_cdata_node((AST_CDATA_NODE_T*) node); break; - case AST_WHITESPACE_NODE: ast_free_whitespace_node((AST_WHITESPACE_NODE_T*) node); break; - case AST_ERB_CONTENT_NODE: ast_free_erb_content_node((AST_ERB_CONTENT_NODE_T*) node); break; - case AST_ERB_END_NODE: ast_free_erb_end_node((AST_ERB_END_NODE_T*) node); break; - case AST_ERB_ELSE_NODE: ast_free_erb_else_node((AST_ERB_ELSE_NODE_T*) node); break; - case AST_ERB_IF_NODE: ast_free_erb_if_node((AST_ERB_IF_NODE_T*) node); break; - case AST_ERB_BLOCK_NODE: ast_free_erb_block_node((AST_ERB_BLOCK_NODE_T*) node); break; - case AST_ERB_WHEN_NODE: ast_free_erb_when_node((AST_ERB_WHEN_NODE_T*) node); break; - case AST_ERB_CASE_NODE: ast_free_erb_case_node((AST_ERB_CASE_NODE_T*) node); break; - case AST_ERB_CASE_MATCH_NODE: ast_free_erb_case_match_node((AST_ERB_CASE_MATCH_NODE_T*) node); break; - case AST_ERB_WHILE_NODE: ast_free_erb_while_node((AST_ERB_WHILE_NODE_T*) node); break; - case AST_ERB_UNTIL_NODE: ast_free_erb_until_node((AST_ERB_UNTIL_NODE_T*) node); break; - case AST_ERB_FOR_NODE: ast_free_erb_for_node((AST_ERB_FOR_NODE_T*) node); break; - case AST_ERB_RESCUE_NODE: ast_free_erb_rescue_node((AST_ERB_RESCUE_NODE_T*) node); break; - case AST_ERB_ENSURE_NODE: ast_free_erb_ensure_node((AST_ERB_ENSURE_NODE_T*) node); break; - case AST_ERB_BEGIN_NODE: ast_free_erb_begin_node((AST_ERB_BEGIN_NODE_T*) node); break; - case AST_ERB_UNLESS_NODE: ast_free_erb_unless_node((AST_ERB_UNLESS_NODE_T*) node); break; - case AST_ERB_YIELD_NODE: ast_free_erb_yield_node((AST_ERB_YIELD_NODE_T*) node); break; - case AST_ERB_IN_NODE: ast_free_erb_in_node((AST_ERB_IN_NODE_T*) node); break; - } -} diff --git a/src/ast_pretty_print.c b/src/ast_pretty_print.c deleted file mode 100644 index 05a08a93a..000000000 --- a/src/ast_pretty_print.c +++ /dev/null @@ -1,657 +0,0 @@ -// NOTE: This file is generated by the templates/template.rb script and should not -// be modified manually. See /Users/dani/oss/herb/templates/src/ast_pretty_print.c.erb - -#include "include/ast_node.h" -#include "include/ast_nodes.h" -#include "include/errors.h" -#include "include/pretty_print.h" -#include "include/token_struct.h" -#include "include/util.h" -#include "include/util/hb_buffer.h" - -#include -#include -#include - -void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) { - if (!node) { return; } - - bool print_location = true; - - hb_buffer_append(buffer, "@ "); - hb_buffer_append_string(buffer, ast_node_human_type(node)); - hb_buffer_append(buffer, " "); - - if (print_location) { pretty_print_location(node->location, buffer); } - - hb_buffer_append(buffer, "\n"); - - switch (node->type) { - case AST_DOCUMENT_NODE: { - const AST_DOCUMENT_NODE_T* document_node = (AST_DOCUMENT_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), document_node->children, indent, relative_indent, true, buffer); - } break; - - case AST_LITERAL_NODE: { - const AST_LITERAL_NODE_T* literal_node = (AST_LITERAL_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_string_property(hb_string(literal_node->content), hb_string("content"), indent, relative_indent, true, buffer); - } break; - - case AST_HTML_OPEN_TAG_NODE: { - const AST_HTML_OPEN_TAG_NODE_T* html_open_tag_node = (AST_HTML_OPEN_TAG_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(html_open_tag_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(html_open_tag_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); - pretty_print_token_property(html_open_tag_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_open_tag_node->children, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("is_void"), html_open_tag_node->is_void, indent, relative_indent, true, buffer); - } break; - - case AST_HTML_CLOSE_TAG_NODE: { - const AST_HTML_CLOSE_TAG_NODE_T* html_close_tag_node = (AST_HTML_CLOSE_TAG_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(html_close_tag_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(html_close_tag_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_close_tag_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(html_close_tag_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_HTML_ELEMENT_NODE: { - const AST_HTML_ELEMENT_NODE_T* html_element_node = (AST_HTML_ELEMENT_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("open_tag"), indent, relative_indent, false, buffer); - - if (html_element_node->open_tag) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) html_element_node->open_tag, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - pretty_print_token_property(html_element_node->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("body"), html_element_node->body, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("close_tag"), indent, relative_indent, false, buffer); - - if (html_element_node->close_tag) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) html_element_node->close_tag, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - pretty_print_boolean_property(hb_string("is_void"), html_element_node->is_void, indent, relative_indent, false, buffer); - pretty_print_string_property(element_source_to_string(html_element_node->source), hb_string("source"), indent, relative_indent, true, buffer); - } break; - - case AST_HTML_ATTRIBUTE_VALUE_NODE: { - const AST_HTML_ATTRIBUTE_VALUE_NODE_T* html_attribute_value_node = (AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(html_attribute_value_node->open_quote, hb_string("open_quote"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_attribute_value_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(html_attribute_value_node->close_quote, hb_string("close_quote"), indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("quoted"), html_attribute_value_node->quoted, indent, relative_indent, true, buffer); - } break; - - case AST_HTML_ATTRIBUTE_NAME_NODE: { - const AST_HTML_ATTRIBUTE_NAME_NODE_T* html_attribute_name_node = (AST_HTML_ATTRIBUTE_NAME_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_attribute_name_node->children, indent, relative_indent, true, buffer); - } break; - - case AST_HTML_ATTRIBUTE_NODE: { - const AST_HTML_ATTRIBUTE_NODE_T* html_attribute_node = (AST_HTML_ATTRIBUTE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("name"), indent, relative_indent, false, buffer); - - if (html_attribute_node->name) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) html_attribute_node->name, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - pretty_print_token_property(html_attribute_node->equals, hb_string("equals"), indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("value"), indent, relative_indent, true, buffer); - - if (html_attribute_node->value) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) html_attribute_node->value, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_HTML_TEXT_NODE: { - const AST_HTML_TEXT_NODE_T* html_text_node = (AST_HTML_TEXT_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_string_property(hb_string(html_text_node->content), hb_string("content"), indent, relative_indent, true, buffer); - } break; - - case AST_HTML_COMMENT_NODE: { - const AST_HTML_COMMENT_NODE_T* html_comment_node = (AST_HTML_COMMENT_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(html_comment_node->comment_start, hb_string("comment_start"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_comment_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(html_comment_node->comment_end, hb_string("comment_end"), indent, relative_indent, true, buffer); - } break; - - case AST_HTML_DOCTYPE_NODE: { - const AST_HTML_DOCTYPE_NODE_T* html_doctype_node = (AST_HTML_DOCTYPE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(html_doctype_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), html_doctype_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(html_doctype_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_XML_DECLARATION_NODE: { - const AST_XML_DECLARATION_NODE_T* xml_declaration_node = (AST_XML_DECLARATION_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(xml_declaration_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), xml_declaration_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(xml_declaration_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_CDATA_NODE: { - const AST_CDATA_NODE_T* cdata_node = (AST_CDATA_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(cdata_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), cdata_node->children, indent, relative_indent, false, buffer); - pretty_print_token_property(cdata_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_WHITESPACE_NODE: { - const AST_WHITESPACE_NODE_T* whitespace_node = (AST_WHITESPACE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(whitespace_node->value, hb_string("value"), indent, relative_indent, true, buffer); - } break; - - case AST_ERB_CONTENT_NODE: { - const AST_ERB_CONTENT_NODE_T* erb_content_node = (AST_ERB_CONTENT_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_content_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_content_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_content_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - if (erb_content_node->analyzed_ruby) { - pretty_print_boolean_property(hb_string("if_node"), erb_content_node->analyzed_ruby->has_if_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("elsif_node"), erb_content_node->analyzed_ruby->has_elsif_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("else_node"), erb_content_node->analyzed_ruby->has_else_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("end"), erb_content_node->analyzed_ruby->has_end, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("block_node"), erb_content_node->analyzed_ruby->has_block_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("block_closing"), erb_content_node->analyzed_ruby->has_block_closing, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("case_node"), erb_content_node->analyzed_ruby->has_case_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("when_node"), erb_content_node->analyzed_ruby->has_when_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("for_node"), erb_content_node->analyzed_ruby->has_for_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("while_node"), erb_content_node->analyzed_ruby->has_while_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("until_node"), erb_content_node->analyzed_ruby->has_until_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("begin_node"), erb_content_node->analyzed_ruby->has_begin_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("rescue_node"), erb_content_node->analyzed_ruby->has_rescue_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("ensure_node"), erb_content_node->analyzed_ruby->has_ensure_node, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("unless_node"), erb_content_node->analyzed_ruby->has_unless_node, indent, relative_indent, false, buffer); - } else { - pretty_print_label(hb_string("analyzed_ruby"), indent, relative_indent, false, buffer); - hb_buffer_append(buffer, " ∅\n"); - } - - pretty_print_boolean_property(hb_string("parsed"), erb_content_node->parsed, indent, relative_indent, false, buffer); - pretty_print_boolean_property(hb_string("valid"), erb_content_node->valid, indent, relative_indent, true, buffer); - } break; - - case AST_ERB_END_NODE: { - const AST_ERB_END_NODE_T* erb_end_node = (AST_ERB_END_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_end_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_end_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_end_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_ERB_ELSE_NODE: { - const AST_ERB_ELSE_NODE_T* erb_else_node = (AST_ERB_ELSE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_else_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_else_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_else_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_else_node->statements, indent, relative_indent, true, buffer); - } break; - - case AST_ERB_IF_NODE: { - const AST_ERB_IF_NODE_T* erb_if_node = (AST_ERB_IF_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_if_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_if_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_if_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_if_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("subsequent"), indent, relative_indent, false, buffer); - - if (erb_if_node->subsequent) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_if_node->subsequent, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_if_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_if_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_BLOCK_NODE: { - const AST_ERB_BLOCK_NODE_T* erb_block_node = (AST_ERB_BLOCK_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_block_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_block_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_block_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("body"), erb_block_node->body, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_block_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_block_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_WHEN_NODE: { - const AST_ERB_WHEN_NODE_T* erb_when_node = (AST_ERB_WHEN_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_when_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_when_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_when_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_when_node->statements, indent, relative_indent, true, buffer); - } break; - - case AST_ERB_CASE_NODE: { - const AST_ERB_CASE_NODE_T* erb_case_node = (AST_ERB_CASE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), erb_case_node->children, indent, relative_indent, false, buffer); - pretty_print_array(hb_string("conditions"), erb_case_node->conditions, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); - - if (erb_case_node->else_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_case_node->else_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_case_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_case_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_CASE_MATCH_NODE: { - const AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = (AST_ERB_CASE_MATCH_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_match_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_match_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_case_match_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("children"), erb_case_match_node->children, indent, relative_indent, false, buffer); - pretty_print_array(hb_string("conditions"), erb_case_match_node->conditions, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); - - if (erb_case_match_node->else_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->else_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_case_match_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_WHILE_NODE: { - const AST_ERB_WHILE_NODE_T* erb_while_node = (AST_ERB_WHILE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_while_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_while_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_while_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_while_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_while_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_while_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_UNTIL_NODE: { - const AST_ERB_UNTIL_NODE_T* erb_until_node = (AST_ERB_UNTIL_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_until_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_until_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_until_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_until_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_until_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_until_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_FOR_NODE: { - const AST_ERB_FOR_NODE_T* erb_for_node = (AST_ERB_FOR_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_for_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_for_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_for_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_for_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_for_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_for_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_RESCUE_NODE: { - const AST_ERB_RESCUE_NODE_T* erb_rescue_node = (AST_ERB_RESCUE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_rescue_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_rescue_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_rescue_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_rescue_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("subsequent"), indent, relative_indent, true, buffer); - - if (erb_rescue_node->subsequent) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_rescue_node->subsequent, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_ENSURE_NODE: { - const AST_ERB_ENSURE_NODE_T* erb_ensure_node = (AST_ERB_ENSURE_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_ensure_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_ensure_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_ensure_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_ensure_node->statements, indent, relative_indent, true, buffer); - } break; - - case AST_ERB_BEGIN_NODE: { - const AST_ERB_BEGIN_NODE_T* erb_begin_node = (AST_ERB_BEGIN_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_begin_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_begin_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_begin_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_begin_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("rescue_clause"), indent, relative_indent, false, buffer); - - if (erb_begin_node->rescue_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_begin_node->rescue_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); - - if (erb_begin_node->else_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_begin_node->else_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("ensure_clause"), indent, relative_indent, false, buffer); - - if (erb_begin_node->ensure_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_begin_node->ensure_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_begin_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_begin_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_UNLESS_NODE: { - const AST_ERB_UNLESS_NODE_T* erb_unless_node = (AST_ERB_UNLESS_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_unless_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_unless_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_unless_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_unless_node->statements, indent, relative_indent, false, buffer); - - pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer); - - if (erb_unless_node->else_clause) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_unless_node->else_clause, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - - pretty_print_label(hb_string("end_node"), indent, relative_indent, true, buffer); - - if (erb_unless_node->end_node) { - hb_buffer_append(buffer, "\n"); - pretty_print_indent(buffer, indent); - pretty_print_indent(buffer, relative_indent + 1); - - hb_buffer_append(buffer, "└── "); - ast_pretty_print_node((AST_NODE_T*) erb_unless_node->end_node, indent, relative_indent + 2, buffer); - } else { - hb_buffer_append(buffer, " ∅\n"); - } - hb_buffer_append(buffer, "\n"); - - } break; - - case AST_ERB_YIELD_NODE: { - const AST_ERB_YIELD_NODE_T* erb_yield_node = (AST_ERB_YIELD_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_yield_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_yield_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_yield_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer); - } break; - - case AST_ERB_IN_NODE: { - const AST_ERB_IN_NODE_T* erb_in_node = (AST_ERB_IN_NODE_T*) node; - - pretty_print_errors(node, indent, relative_indent, false, buffer); - pretty_print_token_property(erb_in_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_in_node->content, hb_string("content"), indent, relative_indent, false, buffer); - pretty_print_token_property(erb_in_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer); - pretty_print_array(hb_string("statements"), erb_in_node->statements, indent, relative_indent, true, buffer); - } break; - - } -} diff --git a/src/include/ast_nodes.h b/src/include/ast_nodes.h deleted file mode 100644 index e06e8022b..000000000 --- a/src/include/ast_nodes.h +++ /dev/null @@ -1,346 +0,0 @@ -// NOTE: This file is generated by the templates/template.rb script and should not -// be modified manually. See /Users/dani/oss/herb/templates/src/include/ast_nodes.h.erb - -#ifndef HERB_AST_NODES_H -#define HERB_AST_NODES_H - -#include -#include - -#include "analyzed_ruby.h" -#include "element_source.h" -#include "location.h" -#include "position.h" -#include "token_struct.h" -#include "util/hb_array.h" -#include "util/hb_buffer.h" -#include "util/hb_string.h" - -typedef enum { - AST_DOCUMENT_NODE, - AST_LITERAL_NODE, - AST_HTML_OPEN_TAG_NODE, - AST_HTML_CLOSE_TAG_NODE, - AST_HTML_ELEMENT_NODE, - AST_HTML_ATTRIBUTE_VALUE_NODE, - AST_HTML_ATTRIBUTE_NAME_NODE, - AST_HTML_ATTRIBUTE_NODE, - AST_HTML_TEXT_NODE, - AST_HTML_COMMENT_NODE, - AST_HTML_DOCTYPE_NODE, - AST_XML_DECLARATION_NODE, - AST_CDATA_NODE, - AST_WHITESPACE_NODE, - AST_ERB_CONTENT_NODE, - AST_ERB_END_NODE, - AST_ERB_ELSE_NODE, - AST_ERB_IF_NODE, - AST_ERB_BLOCK_NODE, - AST_ERB_WHEN_NODE, - AST_ERB_CASE_NODE, - AST_ERB_CASE_MATCH_NODE, - AST_ERB_WHILE_NODE, - AST_ERB_UNTIL_NODE, - AST_ERB_FOR_NODE, - AST_ERB_RESCUE_NODE, - AST_ERB_ENSURE_NODE, - AST_ERB_BEGIN_NODE, - AST_ERB_UNLESS_NODE, - AST_ERB_YIELD_NODE, - AST_ERB_IN_NODE, -} ast_node_type_T; - -typedef struct AST_NODE_STRUCT { - ast_node_type_T type; - location_T location; - // maybe a range too? - hb_array_T* errors; -} AST_NODE_T; - - -typedef struct AST_DOCUMENT_NODE_STRUCT { - AST_NODE_T base; - hb_array_T* children; -} AST_DOCUMENT_NODE_T; - -typedef struct AST_LITERAL_NODE_STRUCT { - AST_NODE_T base; - const char* content; -} AST_LITERAL_NODE_T; - -typedef struct AST_HTML_OPEN_TAG_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* tag_name; - token_T* tag_closing; - hb_array_T* children; - bool is_void; -} AST_HTML_OPEN_TAG_NODE_T; - -typedef struct AST_HTML_CLOSE_TAG_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* tag_name; - hb_array_T* children; - token_T* tag_closing; -} AST_HTML_CLOSE_TAG_NODE_T; - -typedef struct AST_HTML_ELEMENT_NODE_STRUCT { - AST_NODE_T base; - struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag; - token_T* tag_name; - hb_array_T* body; - struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag; - bool is_void; - element_source_t source; -} AST_HTML_ELEMENT_NODE_T; - -typedef struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT { - AST_NODE_T base; - token_T* open_quote; - hb_array_T* children; - token_T* close_quote; - bool quoted; -} AST_HTML_ATTRIBUTE_VALUE_NODE_T; - -typedef struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT { - AST_NODE_T base; - hb_array_T* children; -} AST_HTML_ATTRIBUTE_NAME_NODE_T; - -typedef struct AST_HTML_ATTRIBUTE_NODE_STRUCT { - AST_NODE_T base; - struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name; - token_T* equals; - struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value; -} AST_HTML_ATTRIBUTE_NODE_T; - -typedef struct AST_HTML_TEXT_NODE_STRUCT { - AST_NODE_T base; - const char* content; -} AST_HTML_TEXT_NODE_T; - -typedef struct AST_HTML_COMMENT_NODE_STRUCT { - AST_NODE_T base; - token_T* comment_start; - hb_array_T* children; - token_T* comment_end; -} AST_HTML_COMMENT_NODE_T; - -typedef struct AST_HTML_DOCTYPE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - hb_array_T* children; - token_T* tag_closing; -} AST_HTML_DOCTYPE_NODE_T; - -typedef struct AST_XML_DECLARATION_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - hb_array_T* children; - token_T* tag_closing; -} AST_XML_DECLARATION_NODE_T; - -typedef struct AST_CDATA_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - hb_array_T* children; - token_T* tag_closing; -} AST_CDATA_NODE_T; - -typedef struct AST_WHITESPACE_NODE_STRUCT { - AST_NODE_T base; - token_T* value; -} AST_WHITESPACE_NODE_T; - -typedef struct AST_ERB_CONTENT_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - analyzed_ruby_T* analyzed_ruby; - bool parsed; - bool valid; -} AST_ERB_CONTENT_NODE_T; - -typedef struct AST_ERB_END_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; -} AST_ERB_END_NODE_T; - -typedef struct AST_ERB_ELSE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; -} AST_ERB_ELSE_NODE_T; - -typedef struct AST_ERB_IF_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_NODE_STRUCT* subsequent; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_IF_NODE_T; - -typedef struct AST_ERB_BLOCK_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* body; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_BLOCK_NODE_T; - -typedef struct AST_ERB_WHEN_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; -} AST_ERB_WHEN_NODE_T; - -typedef struct AST_ERB_CASE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* children; - hb_array_T* conditions; - struct AST_ERB_ELSE_NODE_STRUCT* else_clause; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_CASE_NODE_T; - -typedef struct AST_ERB_CASE_MATCH_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* children; - hb_array_T* conditions; - struct AST_ERB_ELSE_NODE_STRUCT* else_clause; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_CASE_MATCH_NODE_T; - -typedef struct AST_ERB_WHILE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_WHILE_NODE_T; - -typedef struct AST_ERB_UNTIL_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_UNTIL_NODE_T; - -typedef struct AST_ERB_FOR_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_FOR_NODE_T; - -typedef struct AST_ERB_RESCUE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_RESCUE_NODE_STRUCT* subsequent; -} AST_ERB_RESCUE_NODE_T; - -typedef struct AST_ERB_ENSURE_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; -} AST_ERB_ENSURE_NODE_T; - -typedef struct AST_ERB_BEGIN_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause; - struct AST_ERB_ELSE_NODE_STRUCT* else_clause; - struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_BEGIN_NODE_T; - -typedef struct AST_ERB_UNLESS_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; - struct AST_ERB_ELSE_NODE_STRUCT* else_clause; - struct AST_ERB_END_NODE_STRUCT* end_node; -} AST_ERB_UNLESS_NODE_T; - -typedef struct AST_ERB_YIELD_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; -} AST_ERB_YIELD_NODE_T; - -typedef struct AST_ERB_IN_NODE_STRUCT { - AST_NODE_T base; - token_T* tag_opening; - token_T* content; - token_T* tag_closing; - hb_array_T* statements; -} AST_ERB_IN_NODE_T; - -AST_DOCUMENT_NODE_T* ast_document_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors); -AST_LITERAL_NODE_T* ast_literal_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_OPEN_TAG_NODE_T* ast_html_open_tag_node_init(token_T* tag_opening, token_T* tag_name, token_T* tag_closing, hb_array_T* children, bool is_void, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_CLOSE_TAG_NODE_T* ast_html_close_tag_node_init(token_T* tag_opening, token_T* tag_name, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_ELEMENT_NODE_T* ast_html_element_node_init(struct AST_HTML_OPEN_TAG_NODE_STRUCT* open_tag, token_T* tag_name, hb_array_T* body, struct AST_HTML_CLOSE_TAG_NODE_STRUCT* close_tag, bool is_void, element_source_t source, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_ATTRIBUTE_VALUE_NODE_T* ast_html_attribute_value_node_init(token_T* open_quote, hb_array_T* children, token_T* close_quote, bool quoted, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_ATTRIBUTE_NAME_NODE_T* ast_html_attribute_name_node_init(hb_array_T* children, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_ATTRIBUTE_NODE_T* ast_html_attribute_node_init(struct AST_HTML_ATTRIBUTE_NAME_NODE_STRUCT* name, token_T* equals, struct AST_HTML_ATTRIBUTE_VALUE_NODE_STRUCT* value, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_TEXT_NODE_T* ast_html_text_node_init(const char* content, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_COMMENT_NODE_T* ast_html_comment_node_init(token_T* comment_start, hb_array_T* children, token_T* comment_end, position_T start_position, position_T end_position, hb_array_T* errors); -AST_HTML_DOCTYPE_NODE_T* ast_html_doctype_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_XML_DECLARATION_NODE_T* ast_xml_declaration_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_CDATA_NODE_T* ast_cdata_node_init(token_T* tag_opening, hb_array_T* children, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_WHITESPACE_NODE_T* ast_whitespace_node_init(token_T* value, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_CONTENT_NODE_T* ast_erb_content_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, analyzed_ruby_T* analyzed_ruby, bool parsed, bool valid, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_END_NODE_T* ast_erb_end_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* body, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_UNTIL_NODE_T* ast_erb_until_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_FOR_NODE_T* ast_erb_for_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_RESCUE_NODE_T* ast_erb_rescue_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* subsequent, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_ENSURE_NODE_T* ast_erb_ensure_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors); -AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors); - -hb_string_T ast_node_type_to_string(AST_NODE_T* node); -hb_string_T ast_node_human_type(AST_NODE_T* node); - -#endif diff --git a/src/include/ast_pretty_print.h b/src/include/ast_pretty_print.h deleted file mode 100644 index 27010224e..000000000 --- a/src/include/ast_pretty_print.h +++ /dev/null @@ -1,17 +0,0 @@ -// NOTE: This file is generated by the templates/template.rb script and should not -// be modified manually. See /Users/dani/oss/herb/templates/src/include/ast_pretty_print.h.erb - -#ifndef HERB_AST_PRETTY_PRINT_H -#define HERB_AST_PRETTY_PRINT_H - -#include "ast_nodes.h" -#include "util/hb_buffer.h" - -void ast_pretty_print_node( - AST_NODE_T* node, - size_t indent, - size_t relative_indent, - hb_buffer_T* buffer -); - -#endif