Comparing version 0.1.4 to 0.1.5
{ | ||
"name": "jinx-rust", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "Rust parser", | ||
"repository": "https://github.com/jinxdash/jinx-rust", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/jinxdash/jinx-rust.git" | ||
}, | ||
"author": "jinxdash <jinxdash.github@gmail.com> (https://github.com/jinxdash)", | ||
@@ -47,8 +50,8 @@ "keywords": [ | ||
"devDependencies": { | ||
"@types/node": "^18.0.6", | ||
"@types/node": "^18.6.1", | ||
"@types/prettier": "^2.6.3", | ||
"@typescript-eslint/typescript-estree": "^5.30.7", | ||
"@typescript-eslint/typescript-estree": "^5.31.0", | ||
"prettier": "^2.7.1", | ||
"ts-node": "^10.9.1", | ||
"tsup": "^6.1.3", | ||
"tsup": "^6.2.0", | ||
"typescript": "^4.7.4" | ||
@@ -61,4 +64,16 @@ }, | ||
"useTabs": true, | ||
"endOfLine": "lf" | ||
"endOfLine": "lf", | ||
"overrides": [ | ||
{ | ||
"files": [ | ||
"**/*.md" | ||
], | ||
"options": { | ||
"printWidth": 80, | ||
"useTabs": false, | ||
"tabWidth": 2 | ||
} | ||
} | ||
] | ||
} | ||
} |
158
README.md
# jinx-rust · ![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg) [![npm version](https://img.shields.io/npm/v/jinx-rust.svg?style=flat)](https://www.npmjs.com/package/jinx-rust) ![GitHub Repo stars](https://img.shields.io/github/stars/jinxdash/jinx-rust?style=social) [![Twitter Follow](https://img.shields.io/twitter/follow/jinxdash?style=social)](https://twitter.com/jinxdash) | ||
`jinx-rust` is a [Rust](https://www.rust-lang.org/) parser written in Typescript. | ||
`jinx-rust` is a [Rust](https://www.rust-lang.org/) parser written in Typescript; it enables the development of Rust Tooling in Typescript. | ||
Example project using `jinx-rust`: [Prettier Rust formatter](https://github.com/jinxdash/prettier-plugin-rust) | ||
## Get Started | ||
``` | ||
```sh | ||
npm install jinx-rust | ||
@@ -14,46 +16,48 @@ ``` | ||
const file = rs.parseFile(`let foo: u8 = 1;`); | ||
const file = rs.parseFile("let leet: u32 = 1337;"); | ||
console.log(JSON.stringify(file)); | ||
``` | ||
```json | ||
{ | ||
"type": "SourceFile", | ||
"program": { | ||
"type": "Program", | ||
"ast": [ | ||
{ | ||
"type": "LetVariableDeclaration", | ||
"pattern": { "type": "Identifier", "name": "foo" }, | ||
"typeAnnotation": { "type": "Identifier", "name": "u8" }, | ||
"expression": { "type": "Literal", "kind": 11, "value": "1" } | ||
} | ||
], | ||
"danglingAttributes": [], | ||
"comments": [] | ||
} | ||
"type": "SourceFile", | ||
"program": { | ||
"type": "Program", | ||
"ast": [ | ||
{ | ||
"type": "LetVariableDeclaration", | ||
"pattern": { "type": "Identifier", "name": "leet" }, | ||
"typeAnnotation": { "type": "Identifier", "name": "u32" }, | ||
"expression": { "type": "Literal", "kind": 11, "value": "1337" } | ||
} | ||
], | ||
"danglingAttributes": [], | ||
"comments": [] | ||
} | ||
} | ||
``` | ||
## Loose parsing | ||
## A tolerant parser for better Rust Tooling | ||
Though eventually there should be a `strict` parser option to validate the AST, | ||
`jinx-rust` is unstrict by default and tolerates many things: | ||
`jinx-rust` is unstrict by default and tolerates bad syntax | ||
- Missing semicolons | ||
- Missing comas in `match` and declarations | ||
- Labels on blocks that can't have them | ||
- Unsyntactic Attributes and Doc Attributes | ||
- Unsyntactic parentheses in RangePatterns | ||
- Malformed tokens, Javascript's `===` and `!==` | ||
- Closures with a returnType followed by an inlined expression | ||
- Forbidden node types (e.g. expressions in top level, patterns in `const` declarations) | ||
- Missing nodes (e.g. fn `parameter.typeAnnotation`) | ||
- Tokens: | ||
- Missing semicolons | ||
- Missing commas in some places | ||
- Whitespace within long tokens | ||
- Javascript-ey syntax, (currently just `===` and `!==`) | ||
- Unsyntactic parentheses (e.g. RangePattern bounds) | ||
- Closures with a returnType and a non-block expression | ||
- Nodes: | ||
- Unsyntactic Labels, Attributes and Doc Attributes | ||
- Arbitrary specifier order (e.g. `unsafe pub fn ...`) | ||
- Forbidden node types (e.g. patterns in const variables, expressions in top level) | ||
- Missing typeAnnotations | ||
```ts | ||
import { rs } from "jinx-rust"; | ||
// Would not parse in Rust or syn | ||
const arg_0 = rs.parseFile("fn foo(arg_0) {}").program.ast[0].parameters[0]; | ||
@@ -64,2 +68,4 @@ | ||
In the future, `jinx-rust` should eventually get a `strict` option. | ||
## Conversion between nodes and tokens | ||
@@ -73,18 +79,19 @@ | ||
const node = rs.parseFile("foo!(123);").program.ast[0].expression as MacroInvocation; | ||
const node = rs.parseFile("foo!(123);").program.ast[0] | ||
.expression as MacroInvocation; | ||
// ExpressionNode[] | ||
const args = rs.toCallExpressionArguments(node.tokens).ast; | ||
const args = rs.toCallExpressionArguments(node.tokens); // ExpressionNode[] | ||
const block = rs.toBlockBody(node.tokens).ast; // StatementNode[] | ||
// StatementNode[] | ||
const block = rs.toBlockBody(node.tokens).ast; | ||
// TokenNode[] | ||
const tokens = rs.toTokens(node).ast; | ||
const tokens = rs.toTokens(node).ast; // TokenNode[] | ||
``` | ||
## import AST helpers from `"jinx-rust/utils"` | ||
## Nightly features | ||
`jinx-rust/utils` is automatically included on install. It is a library of (mostly) auto-generated helpers from the parser's type declarations. Like `each_node` traversing, or `is_{NodeType}` functions for every node type, and `is_{Type}` for every type exported by the parser. | ||
`jinx-rust` supports 23 nightly features. The full list can be found in `src/parser/nodes.ts` under `enum Feature`. ([link](https://github.com/jinxdash/jinx-rust/blob/5fcd69e007e8401220db94710c5a879d686ee795/src/parser/nodes.ts#L93-L139)) | ||
## `jinx-rust/utils` | ||
`jinx-rust/utils` is automatically included on install. It is a library of (mostly) auto-generated helpers from the parser's type declarations. E.g. tree traversing helpers, `is_{Type}(node)` functions for every type declared exported by the parser. | ||
```ts | ||
@@ -96,26 +103,63 @@ import { each_node, is_StatementNode } from "jinx-rust/utils"; | ||
each_node(target, (child, parent) => { | ||
if (is_StatementNode(child)) { | ||
// gets called for every statement in target | ||
} | ||
if (is_StatementNode(child)) { | ||
// gets called for every statement in target | ||
} | ||
}); | ||
``` | ||
## Gotchas when working with `jinx-rust` | ||
## Gotchas | ||
- When a node has outer attributes, its start location expands to include them, | ||
its own start positions is saved under `node.loc.ownStart` | ||
- When a node has outer attributes, its start location expands to them, and its own start position is saved under `node.loc.ownStart` | ||
```ts | ||
import { Node } from "jinx-rust"; | ||
import { start, end, ownStart, has_OuterAttributes, hasOwnStart } from "jinx-rust/utils"; | ||
* Prefer `jinx-rust/utils` helpers to access locations, e.g. `start(node)` instead of `node.loc[0]` | ||
declare const node: Node; | ||
- Exported `.d.ts` nodes each document their syntax, hit `Go to Definition` or hover over their class to see it. | ||
start(node) === node.loc[0]; end(node) === node.loc[1]; | ||
```ts | ||
import { TraitDeclaration } from "jinx-rust"; | ||
has_OuterAttributes(node) === hasOwnStart(node); | ||
ownStart(node) === (node.loc.ownStart ?? node.loc[0]); | ||
``` | ||
TraitDeclaration; | ||
// ^? import TraitDeclaration | ||
// trait id<...generics>?: ...typeBounds? where ...whereBounds? { ...body } | ||
``` | ||
## Projects using `jinx-rust` | ||
- `filepath` is not required but useful in debugging: `rs.parseFile(code, { filepath })` | ||
- [`prettier-plugin-rust`]("https://github.com/jinxdash/prettier-plugin-rust") | ||
- Using `node.loc.url()` is an easy, quick way to get a clickable link to nodes causing problems. | ||
- To facilitate debugging in NodeJS, `class Loc` implements `nodejs.util.inspect.custom` | ||
Hence in `console.log(node)`, loc is logged as `node.loc.url()` | ||
e.g. `TraiDeclaration { ..., loc: "path/to/file.rs:51:18", ... }` | ||
--- | ||
## Why `jinx-rust`? And why in Typescript? | ||
- ### The case for Rust Tooling to split from rustc | ||
Tooling and compiler should only share the spec in common. Take the tripartite system in Javascript: | ||
- The spec TC39 ("legislative") | ||
- The core V8/JC/SM ("judicial") | ||
- The tooling Typescript ("executive") | ||
Through that lens, Rust is in a rather bleek shape: | ||
- No spec, the 1 implementation _is_ the law | ||
- The core is rustc | ||
- The tooling is done on top of core, sometimes even arbitrarily within core's algorithms | ||
Building Rust Tooling on top of rustc is a mistake akin to building Typescript's editor integration on top of v8's Turbofan. | ||
- ### The case for Core and Tooling to use different Parsers and ASTs | ||
Both have drastically different needs, and attempting to serve both at the same time is grossly counterproductive. | ||
- Core needs a minimalistic, clear and no bs parser & AST that follows the syntax allowed in the spec by the dot and to the dot. Arbitrarily injecting lints in the core parser or bloating its data structure with CST is frankly out of scope and corrupts the entire language foundation. | ||
- Tooling needs a parser that is much more tolerant and flexible, it should be able to overlook or bend the spec syntax rules to best infer user's intent. | ||
- ### The case for writing Rust Tooling in Typescript | ||
Tooling requires a language with a structure that is much more flexible, dynamic and hackable to accomodate for the wide range and open-endedness of the solutions it seeks to provide. Rust is simply not the right pick for the job, Rust is not the right pick for everything damnit. |
@@ -205,2 +205,3 @@ import { | ||
UnaryPattern, | ||
UnaryType, | ||
PostfixExpression, | ||
@@ -487,2 +488,3 @@ VariableDeclarationNode, | ||
declare function is_UnaryPattern(node: Node): node is UnaryPattern; | ||
declare function is_UnaryType(node: Node): node is UnaryType; | ||
declare function is_PostfixExpression(node: Node): node is PostfixExpression; | ||
@@ -834,2 +836,3 @@ declare function is_VariableDeclarationNode(node: Node): node is VariableDeclarationNode; | ||
is_UnaryPattern, | ||
is_UnaryType, | ||
is_UnassignedExpression, | ||
@@ -836,0 +839,0 @@ is_UnionDeclaration, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
626501
21655
162
0