{"_id":"@bakes/dastardly-tree-sitter-runtime","name":"@bakes/dastardly-tree-sitter-runtime","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bakes/dastardly-tree-sitter-runtime","version":"1.0.0","description":"Tree-sitter runtime abstraction and base parser for dASTardly","main":"dist/index.js","types":"dist/index.d.ts","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"keywords":["tree-sitter","parser","ast"],"author":{"name":"The Software Bakery"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/thesoftwarebakery/dastardly.git","directory":"packages/tree-sitter-runtime"},"homepage":"https://github.com/thesoftwarebakery/dastardly#readme","bugs":{"url":"https://github.com/thesoftwarebakery/dastardly/issues"},"publishConfig":{"access":"public"},"dependencies":{"tree-sitter":"^0.21.0","@bakes/dastardly-core":"^1.0.0"},"devDependencies":{"@vitest/ui":"^4.0.8","tree-sitter-json":"^0.24.8","typescript":"^5.3.0","vitest":"^1.6.1"},"scripts":{"build":"tsc","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest"},"_id":"@bakes/dastardly-tree-sitter-runtime@1.0.0","_integrity":"sha512-11gvYFOAV1nGUZSPxo6BBKO2Q+2+EoIzdPfzni83fHMBlEtTaCArdOJ2qwMmshsC7x0N+5n+6AhfSa6XSyWgYg==","_resolved":"/tmp/23b65cea2a0897e3bf4159f22f8bcbb4/bakes-dastardly-tree-sitter-runtime-1.0.0.tgz","_from":"file:bakes-dastardly-tree-sitter-runtime-1.0.0.tgz","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-11gvYFOAV1nGUZSPxo6BBKO2Q+2+EoIzdPfzni83fHMBlEtTaCArdOJ2qwMmshsC7x0N+5n+6AhfSa6XSyWgYg==","shasum":"fef6402077ec3ca1f93006a8d5a74d09399cc29b","tarball":"https://registry.npmjs.org/@bakes/dastardly-tree-sitter-runtime/-/dastardly-tree-sitter-runtime-1.0.0.tgz","fileCount":27,"unpackedSize":34137,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bakes%2fdastardly-tree-sitter-runtime@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC/dC5cMhBz3Pw8FVSw4ntJPuet9QQx9g+L+6YcyFB1PAIhAPI3mwqBFWcta5ILrRV5xl+LmsdRMFMJ4GlCkeiNELmT"}]},"_npmUser":{"name":"georgewaters","email":"george@bakes.software"},"directories":{},"maintainers":[{"name":"georgewaters","email":"george@bakes.software"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dastardly-tree-sitter-runtime_1.0.0_1763161251144_0.19305772667707255"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-14T23:00:50.948Z","1.0.0":"2025-11-14T23:00:51.422Z","modified":"2025-11-14T23:00:52.114Z"},"maintainers":[{"name":"georgewaters","email":"george@bakes.software"}],"description":"Tree-sitter runtime abstraction and base parser for dASTardly","homepage":"https://github.com/thesoftwarebakery/dastardly#readme","keywords":["tree-sitter","parser","ast"],"repository":{"type":"git","url":"git+https://github.com/thesoftwarebakery/dastardly.git","directory":"packages/tree-sitter-runtime"},"author":{"name":"The Software Bakery"},"bugs":{"url":"https://github.com/thesoftwarebakery/dastardly/issues"},"license":"MIT","readme":"# @bakes/dastardly-tree-sitter-runtime\n\nTree-sitter runtime abstraction and base parser for dASTardly.\n\n## Installation\n\n```bash\nnpm install @bakes/dastardly-tree-sitter-runtime\n```\n\n```bash\npnpm add @bakes/dastardly-tree-sitter-runtime\n```\n\n## Overview\n\n`@bakes/dastardly-tree-sitter-runtime` provides an abstraction layer over tree-sitter parsers, making it easy to build format-specific parsers that convert tree-sitter's Concrete Syntax Tree (CST) to dASTardly's Abstract Syntax Tree (AST).\n\n**Key Features:**\n- **Runtime abstraction** - Works with both Node.js tree-sitter and web-tree-sitter (WASM)\n- **Base parser class** - Extend `TreeSitterParser` to build format parsers\n- **Position utilities** - Convert tree-sitter positions to dASTardly locations\n- **Error handling** - `ParseError` with source location information\n- **Type-safe** - Full TypeScript support with strict mode\n- **Incremental parsing foundation** - Internal support for future incremental parsing API\n\n## Quick Start\n\n### Using the Base Parser\n\nExtend `TreeSitterParser` to create a format-specific parser:\n\n```typescript\nimport {\n  TreeSitterParser,\n  NodeTreeSitterRuntime,\n  type DocumentNode,\n  type SyntaxNode,\n  type ParserRuntime,\n  type Language,\n  nodeToLocation,\n} from '@bakes/dastardly-tree-sitter-runtime';\nimport { documentNode, stringNode } from '@bakes/dastardly-core';\nimport MY_LANGUAGE from 'tree-sitter-my-format';\n\nexport class MyFormatParser extends TreeSitterParser {\n  constructor(runtime: ParserRuntime, language: Language) {\n    super(runtime, language, 'my-format');\n  }\n\n  protected convertDocument(node: SyntaxNode, source: string): DocumentNode {\n    const loc = nodeToLocation(node, this.sourceFormat);\n    // Convert tree-sitter CST to dASTardly AST\n    const body = this.convertValue(node, source);\n    return documentNode(body, loc);\n  }\n\n  private convertValue(node: SyntaxNode, source: string) {\n    // Implementation specific to your format\n    const loc = nodeToLocation(node, this.sourceFormat);\n    return stringNode(node.text, loc);\n  }\n}\n\n// Usage\nconst runtime = new NodeTreeSitterRuntime();\nconst parser = new MyFormatParser(runtime, MY_LANGUAGE);\nconst ast = parser.parse('source code here');\n```\n\n## API Reference\n\n### Runtime\n\n#### `NodeTreeSitterRuntime`\n\nRuntime implementation for Node.js using the native tree-sitter binding:\n\n```typescript\nimport { NodeTreeSitterRuntime } from '@bakes/dastardly-tree-sitter-runtime';\n\nconst runtime = new NodeTreeSitterRuntime();\n```\n\n**Methods:**\n- `setLanguage(language: Language)` - Set the parser language\n- `parse(source: string)` - Parse source into a syntax tree\n\n### Base Parser\n\n#### `TreeSitterParser`\n\nAbstract base class for format-specific parsers:\n\n```typescript\nabstract class TreeSitterParser {\n  protected readonly runtime: ParserRuntime;\n  protected readonly sourceFormat: string;\n\n  constructor(\n    runtime: ParserRuntime,\n    language: Language,\n    sourceFormat: string\n  );\n\n  parse(source: string): DocumentNode;\n\n  protected abstract convertDocument(\n    node: SyntaxNode,\n    source: string\n  ): DocumentNode;\n}\n```\n\n**Usage:**\n1. Extend the class\n2. Implement `convertDocument()` to convert CST to AST\n3. Call `parse()` to parse source strings\n\n### Types\n\n#### `SyntaxNode`\n\nTree-sitter syntax tree node:\n\n```typescript\ninterface SyntaxNode {\n  readonly type: string;\n  readonly text: string;\n  readonly startPosition: TreeSitterPoint;\n  readonly endPosition: TreeSitterPoint;\n  readonly startIndex: number;\n  readonly endIndex: number;\n  readonly childCount: number;\n  readonly children: readonly SyntaxNode[];\n\n  hasError(): boolean;\n  child(index: number): SyntaxNode | null;\n  childForFieldName(name: string): SyntaxNode | null;\n  namedChild(index: number): SyntaxNode | null;\n  // ... additional methods\n}\n```\n\n#### `TreeSitterPoint`\n\nPosition in tree-sitter format:\n\n```typescript\ninterface TreeSitterPoint {\n  readonly row: number;    // 0-indexed line\n  readonly column: number; // 0-indexed column\n}\n```\n\n#### `SyntaxTree`\n\nTree-sitter syntax tree:\n\n```typescript\ninterface SyntaxTree {\n  readonly rootNode: SyntaxNode;\n  edit(edit: Edit): void; // For incremental parsing (internal)\n}\n```\n\n#### `ParserRuntime`\n\nAbstract parser runtime interface:\n\n```typescript\ninterface ParserRuntime {\n  setLanguage(language: Language): void;\n  parse(source: string): SyntaxTree;\n}\n```\n\n### Utilities\n\n#### `nodeToLocation(node, source)`\n\nConvert tree-sitter node to dASTardly SourceLocation:\n\n```typescript\nimport { nodeToLocation } from '@bakes/dastardly-tree-sitter-runtime';\n\nconst loc = nodeToLocation(syntaxNode, 'json');\n// Returns: SourceLocation with line, column, offset\n```\n\n#### `pointToPosition(point, offset)`\n\nConvert tree-sitter point to dASTardly Position:\n\n```typescript\nimport { pointToPosition } from '@bakes/dastardly-tree-sitter-runtime';\n\nconst pos = pointToPosition(\n  { row: 0, column: 5 },\n  5\n);\n// Returns: Position { line: 1, column: 5, offset: 5 }\n// Note: line is 1-indexed in Position, 0-indexed in TreeSitterPoint\n```\n\n#### `hasError(node)`\n\nCheck if a node or its descendants have errors:\n\n```typescript\nimport { hasError } from '@bakes/dastardly-tree-sitter-runtime';\n\nif (hasError(tree.rootNode)) {\n  console.error('Parse errors detected');\n}\n```\n\n#### `findErrorNode(node)`\n\nFind the first error node in the tree:\n\n```typescript\nimport { findErrorNode } from '@bakes/dastardly-tree-sitter-runtime';\n\nconst errorNode = findErrorNode(tree.rootNode);\nif (errorNode) {\n  console.error('Error at:', errorNode.startPosition);\n}\n```\n\n### Errors\n\n#### `ParseError`\n\nError thrown when parsing fails:\n\n```typescript\nclass ParseError extends Error {\n  constructor(\n    message: string,\n    public readonly loc: SourceLocation,\n    public readonly source: string\n  );\n}\n```\n\n**Usage:**\n\n```typescript\nimport { ParseError, nodeToLocation } from '@bakes/dastardly-tree-sitter-runtime';\n\nif (tree.rootNode.hasError()) {\n  throw new ParseError(\n    'Invalid syntax',\n    nodeToLocation(tree.rootNode, 'json'),\n    source\n  );\n}\n```\n\n## Examples\n\n### Complete Parser Implementation\n\n```typescript\nimport {\n  TreeSitterParser,\n  NodeTreeSitterRuntime,\n  type DocumentNode,\n  type DataNode,\n  type SyntaxNode,\n  type ParserRuntime,\n  type Language,\n  nodeToLocation,\n  ParseError,\n} from '@bakes/dastardly-tree-sitter-runtime';\nimport {\n  documentNode,\n  objectNode,\n  arrayNode,\n  stringNode,\n  numberNode,\n  propertyNode,\n  type PropertyNode,\n} from '@bakes/dastardly-core';\nimport JSON_LANGUAGE from 'tree-sitter-json';\n\nexport class JSONParser extends TreeSitterParser {\n  constructor(runtime: ParserRuntime, language: Language) {\n    super(runtime, language, 'json');\n  }\n\n  protected convertDocument(node: SyntaxNode, source: string): DocumentNode {\n    const loc = nodeToLocation(node, this.sourceFormat);\n\n    // Get the value node (skip whitespace/comments)\n    const valueNode = node.namedChild(0);\n    if (!valueNode) {\n      throw new ParseError('Empty document', loc, source);\n    }\n\n    const body = this.convertValue(valueNode, source);\n    return documentNode(body, loc);\n  }\n\n  private convertValue(node: SyntaxNode, source: string): DataNode {\n    const loc = nodeToLocation(node, this.sourceFormat);\n\n    switch (node.type) {\n      case 'object':\n        return this.convertObject(node, source);\n      case 'array':\n        return this.convertArray(node, source);\n      case 'string':\n        return stringNode(this.unescapeString(node.text), loc, node.text);\n      case 'number':\n        return numberNode(Number(node.text), loc, node.text);\n      // ... handle other types\n      default:\n        throw new ParseError(`Unknown node type: ${node.type}`, loc, source);\n    }\n  }\n\n  private convertObject(node: SyntaxNode, source: string) {\n    const loc = nodeToLocation(node, this.sourceFormat);\n    const properties: PropertyNode[] = [];\n\n    for (const child of node.children) {\n      if (child.type === 'pair') {\n        properties.push(this.convertPair(child, source));\n      }\n    }\n\n    return objectNode(properties, loc);\n  }\n\n  private convertPair(node: SyntaxNode, source: string): PropertyNode {\n    const loc = nodeToLocation(node, this.sourceFormat);\n    const keyNode = node.childForFieldName('key');\n    const valueNode = node.childForFieldName('value');\n\n    if (!keyNode || !valueNode) {\n      throw new ParseError('Invalid pair', loc, source);\n    }\n\n    const key = this.convertValue(keyNode, source);\n    const value = this.convertValue(valueNode, source);\n\n    return propertyNode(key, value, loc);\n  }\n\n  private unescapeString(text: string): string {\n    // Implementation for unescaping JSON strings\n    return text.slice(1, -1); // Simplified\n  }\n}\n```\n\n### Using the Parser\n\n```typescript\nimport { NodeTreeSitterRuntime } from '@bakes/dastardly-tree-sitter-runtime';\nimport { JSONParser } from './json-parser';\nimport JSON_LANGUAGE from 'tree-sitter-json';\n\nconst runtime = new NodeTreeSitterRuntime();\nconst parser = new JSONParser(runtime, JSON_LANGUAGE);\n\ntry {\n  const ast = parser.parse('{\"name\": \"Alice\", \"age\": 30}');\n  console.log(ast.body.type); // 'Object'\n} catch (error) {\n  if (error instanceof ParseError) {\n    console.error('Parse error at line', error.loc.start.line);\n    console.error(error.message);\n  }\n}\n```\n\n### Reusing Parser Instances\n\nParser instances can be reused for better performance:\n\n```typescript\nconst runtime = new NodeTreeSitterRuntime();\nconst parser = new JSONParser(runtime, JSON_LANGUAGE);\n\n// Parse multiple sources with the same parser\nconst doc1 = parser.parse('{\"a\": 1}');\nconst doc2 = parser.parse('{\"b\": 2}');\nconst doc3 = parser.parse('{\"c\": 3}');\n```\n\n### Error Handling\n\n```typescript\nimport { ParseError, hasError, findErrorNode } from '@bakes/dastardly-tree-sitter-runtime';\n\nconst tree = runtime.parse(source);\n\nif (hasError(tree.rootNode)) {\n  const errorNode = findErrorNode(tree.rootNode);\n  if (errorNode) {\n    throw new ParseError(\n      `Syntax error: unexpected ${errorNode.type}`,\n      nodeToLocation(errorNode, 'json'),\n      source\n    );\n  }\n}\n```\n\n## Design Patterns\n\n### Runtime Abstraction\n\nThe `ParserRuntime` interface allows swapping between Node.js and browser implementations:\n\n```typescript\n// Node.js\nconst runtime = new NodeTreeSitterRuntime();\n\n// Future: Browser (WASM)\n// const runtime = new WasmTreeSitterRuntime();\n\nconst parser = new MyFormatParser(runtime, MY_LANGUAGE);\n```\n\n### Fail-Fast Parsing\n\nThe base parser throws errors immediately on parse failure:\n\n```typescript\ntry {\n  const ast = parser.parse(invalidSource);\n} catch (error) {\n  // Handle parse error with location info\n}\n```\n\n### Position Conversion\n\nTree-sitter uses 0-indexed lines, dASTardly uses 1-indexed:\n\n```typescript\n// Tree-sitter: { row: 0, column: 5 }\n// dASTardly:  { line: 1, column: 5, offset: 5 }\n\nconst pos = pointToPosition({ row: 0, column: 5 }, 5);\nconsole.log(pos.line); // 1 (converted to 1-indexed)\n```\n\n## Future Features\n\n### Incremental Parsing (v2)\n\nThe foundation for incremental parsing is in place but not exposed in v1. Future versions will support:\n\n```typescript\n// Future API (not yet available)\nconst edit: Edit = {\n  startIndex: 10,\n  oldEndIndex: 15,\n  newEndIndex: 13,\n  // ... position info\n};\n\nconst doc = parser.parseIncremental(newSource, edit);\n```\n\n## Related Packages\n\n- **[@bakes/dastardly-core](https://www.npmjs.com/package/@bakes/dastardly-core)** - Core AST types\n- **[@bakes/dastardly-json](https://www.npmjs.com/package/@bakes/dastardly-json)** - JSON parser using this runtime\n- **[@bakes/dastardly-yaml](https://www.npmjs.com/package/@bakes/dastardly-yaml)** - YAML parser (coming soon)\n\n## Documentation\n\nFor more information:\n- [Main Repository](https://github.com/thesoftwarebakery/dastardly)\n- [Architecture Documentation](https://github.com/thesoftwarebakery/dastardly/blob/main/ARCHITECTURE.md)\n- [Implementation Guide](https://github.com/thesoftwarebakery/dastardly/blob/main/IMPLEMENTATION_GUIDE.md)\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-f98f90101fd20b0c580810d6962c4315"}