A standard-compliant JavaScript parser, which is a fork of Esprima.NET combined with the .NET port of the acornjs parser.
$ dotnet add package AcornimaThis project is an interbreeding of the acornjs and the Esprima.NET parsers, with the intention of creating an even more complete and performant ECMAScript (a.k.a JavaScript) parser library for .NET by combining the best bits of those.
It should also be mentioned that there is an earlier .NET port of acornjs, AcornSharp, which though is unmaintained for a long time, served as a good starting point. If it weren't for AcornSharp, this project probably have never started.
Node [x]
├─ArrayPattern : IDestructuringPattern [v,s]
├─AssignmentPattern : IDestructuringPattern [v,s]
├─CatchClause [v,s]
├─ClassBody [v,s]
├─ClassProperty : IClassElement, IProperty
│ ├─AccessorProperty : IClassElement, IProperty [v,s]
│ ├─MethodDefinition : IClassElement, IProperty [v,s]
│ └─PropertyDefinition : IClassElement, IProperty [v,s]
├─Decorator [v,s]
├─ImportAttribute [v,s]
├─ModuleSpecifier
│ ├─ExportSpecifier [v,s]
│ └─ImportDeclarationSpecifier
│ ├─ImportDefaultSpecifier [v,s]
│ ├─ImportNamespaceSpecifier [v,s]
│ └─ImportSpecifier [v,s]
├─ObjectPattern : IDestructuringPattern [v,s]
├─Program : IVarScope [v]
│ ├─Module : IVarScope [s]
│ └─Script : IVarScope [s]
├─Property : IProperty [v]
│ ├─AssignmentProperty : IProperty [s]
│ └─ObjectProperty : IProperty [s]
├─RestElement : IDestructuringPattern [v,s]
├─StatementOrExpression
│ ├─Expression [x]
│ │ ├─ArrayExpression [v,s]
│ │ ├─ArrowFunctionExpression : IFunction [v,s]
│ │ ├─AssignmentExpression [v,s]
│ │ ├─AwaitExpression [v,s]
│ │ ├─BinaryExpression [v]
│ │ │ ├─LogicalExpression [s]
│ │ │ └─NonLogicalBinaryExpression [s]
│ │ ├─CallExpression : IChainElement [v,s]
│ │ ├─ChainExpression [v,s]
│ │ ├─ClassExpression : IClass [v,s]
│ │ ├─ConditionalExpression [v,s]
│ │ ├─FunctionExpression : IFunction [v,s]
│ │ ├─Identifier : IDestructuringPattern [v,s]
│ │ ├─ImportExpression [v,s]
│ │ ├─Literal [v]
│ │ │ ├─BigIntLiteral [s]
│ │ │ ├─BooleanLiteral [s]
│ │ │ ├─NullLiteral [s]
│ │ │ ├─NumericLiteral [s]
│ │ │ ├─RegExpLiteral [s]
│ │ │ └─StringLiteral [s]
│ │ ├─MemberExpression : IChainElement, IDestructuringPattern [v,s]
│ │ ├─MetaProperty [v,s]
│ │ ├─NewExpression [v,s]
│ │ ├─ObjectExpression [v,s]
│ │ ├─ParenthesizedExpression [v,s]
│ │ ├─PrivateIdentifier [v,s]
│ │ ├─SequenceExpression [v,s]
│ │ ├─SpreadElement [v,s]
│ │ ├─Super [v,s]
│ │ ├─TaggedTemplateExpression [v,s]
│ │ ├─TemplateLiteral [v,s]
│ │ ├─ThisExpression [v,s]
│ │ ├─UnaryExpression [v]
│ │ │ ├─NonUpdateUnaryExpression [s]
│ │ │ └─UpdateExpression [s]
│ │ └─YieldExpression [v,s]
│ └─Statement [x]
│ ├─BlockStatement [v]
│ │ ├─FunctionBody : IVarScope [s]
│ │ ├─NestedBlockStatement [s]
│ │ └─StaticBlock : IClassElement, IVarScope [v,s]
│ ├─BreakStatement [v,s]
│ ├─ContinueStatement [v,s]
│ ├─DebuggerStatement [v,s]
│ ├─Declaration [x]
│ │ ├─ClassDeclaration : IClass [v,s]
│ │ ├─FunctionDeclaration : IFunction [v,s]
│ │ ├─ImportOrExportDeclaration
│ │ │ ├─ExportDeclaration
│ │ │ │ ├─ExportAllDeclaration [v,s]
│ │ │ │ ├─ExportDefaultDeclaration [v,s]
│ │ │ │ └─ExportNamedDeclaration [v,s]
│ │ │ └─ImportDeclaration [v,s]
│ │ └─VariableDeclaration [v,s]
│ ├─DoWhileStatement [v,s]
│ ├─EmptyStatement [v,s]
│ ├─ExpressionStatement [v]
│ │ ├─Directive [s]
│ │ └─NonSpecialExpressionStatement [s]
│ ├─ForInStatement [v,s]
│ ├─ForOfStatement [v,s]
│ ├─ForStatement [v,s]
│ ├─IfStatement [v,s]
│ ├─LabeledStatement [v,s]
│ ├─ReturnStatement [v,s]
│ ├─SwitchStatement [v,s]
│ ├─ThrowStatement [v,s]
│ ├─TryStatement [v,s]
│ ├─WhileStatement [v,s]
│ └─WithStatement [v,s]
├─SwitchCase [v,s]
├─TemplateElement [v,s]
└─VariableDeclarator [v,s]
Legend:
v - A visitation method is generated in the visitors for the node type.s - The node class is sealed. (It's beneficial to check for sealed types when possible.)x - The node class can be subclassed. (The AST provides some limited extensibility for special use cases.)| Method | Runtime | FileName | Mean | Allocated |
|---|---|---|---|---|
| Acornima-dev | .NET 8.0 | angular-1.2.5 | 10.576 ms | 4062.79 KB |
| Acornima-dev | .NET Framework 4.8 | angular-1.2.5 | 21.935 ms | 4083.74 KB |
| Esprima-v3.0.4 | .NET 8.0 | angular-1.2.5 | 11.214 ms | 3828.11 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | angular-1.2.5 | 20.684 ms | 3879.54 KB |
| Acornima-dev | .NET 8.0 | backbone-1.1.0 | 1.408 ms | 638.72 KB |
| Acornima-dev | .NET Framework 4.8 | backbone-1.1.0 | 3.226 ms | 642.58 KB |
| Esprima-v3.0.4 | .NET 8.0 | backbone-1.1.0 | 1.465 ms | 613.88 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | backbone-1.1.0 | 2.917 ms | 620.3 KB |
| Acornima-dev | .NET 8.0 | jquery-1.9.1 | 8.221 ms | 3322.58 KB |
| Acornima-dev | .NET Framework 4.8 | jquery-1.9.1 | 18.009 ms | 3339.42 KB |
| Esprima-v3.0.4 | .NET 8.0 | jquery-1.9.1 | 8.469 ms | 3305.23 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | jquery-1.9.1 | 16.542 ms | 3355.15 KB |
| Acornima-dev | .NET 8.0 | jquery.mobile-1.4.2 | 14.038 ms | 5499.24 KB |
| Acornima-dev | .NET Framework 4.8 | jquery.mobile-1.4.2 | 29.629 ms | 5530.42 KB |
| Esprima-v3.0.4 | .NET 8.0 | jquery.mobile-1.4.2 | 14.599 ms | 5428.48 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | jquery.mobile-1.4.2 | 27.261 ms | 5497.48 KB |
| Acornima-dev | .NET 8.0 | mootools-1.4.5 | 6.695 ms | 2812.26 KB |
| Acornima-dev | .NET Framework 4.8 | mootools-1.4.5 | 14.633 ms | 2828 KB |
| Esprima-v3.0.4 | .NET 8.0 | mootools-1.4.5 | 7.034 ms | 2777.83 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | mootools-1.4.5 | 13.754 ms | 2816.33 KB |
| Acornima-dev | .NET 8.0 | underscore-1.5.2 | 1.158 ms | 541.81 KB |
| Acornima-dev | .NET Framework 4.8 | underscore-1.5.2 | 2.782 ms | 544.51 KB |
| Esprima-v3.0.4 | .NET 8.0 | underscore-1.5.2 | 1.229 ms | 539.42 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | underscore-1.5.2 | 2.516 ms | 547.18 KB |
| Acornima-dev | .NET 8.0 | yui-3.12.0 | 5.867 ms | 2638.28 KB |
| Acornima-dev | .NET Framework 4.8 | yui-3.12.0 | 13.651 ms | 2655.09 KB |
| Esprima-v3.0.4 | .NET 8.0 | yui-3.12.0 | 6.488 ms | 2585.78 KB |
| Esprima-v3.0.4 | .NET Framework 4.8 | yui-3.12.0 | 12.365 ms | 2624.92 KB |
The parser can be configured to convert JS regular expression literals to .NET Regex instances (see ParserOptions.RegExpParseMode).
However, because of the fundamental differences between the JS and .NET regex engines, in many cases this conversion can't be done perfectly (or, in some cases, at all):
RegExpParseResult object's ActualRegexGroupCount property.$group is valid in JS but invalid in .NET. So, as a workaround, the converter encodes the problematic group names to names that are valid in .NET and probably won't collide with other group names present in the pattern. For example, $group is encoded like __utf8_2467726F7570. The original group names can be obtained using the returned RegExpParseResult object's GetRegexGroupName method./((a+)(\1) ?)+/ may not produce the exact same captures. RegexOptions.ECMAScript is supposed to cover this, however even the MSDN example doesn't produce the same matches. (As a side note, RegexOptions.ECMAScript is kinda a false promise, it can't even get some basic cases right by itself.)/((a+)?(b+)?(c))*/ may produce different captures for the groups. (JS has an overwrite behavior while .NET doesn't).\1(\w) differently than JS and it's not possible to convert this kind of patterns reliably. (The converter could make some patterns work by rewriting them to something like (?:)(\w) but there are cases where even this wouldn't work.)/a?/u will cause this issue when the input string contains astral Unicode chars. There is no out-of-the-box workaround for this issue but it can be mitigated somewhat using a bit of "post-processing", i.e., by filtering out the false positive matches after evaluation like it's done here. Probably there is no way to improve this situation until .NET adds the option to treat the input string as Unicode code points.To sum it up, legacy pattern conversion is pretty solid apart from the minor issues listed above. However, support for unicode mode (flag u) patterns is partial and quirky, while conversion of the upcoming unicode sets mode (flag v) will be next to impossible - until the .NET regex engine gets some improved Unicode support.