Merge histories of babylon and babel

This commit is contained in:
Daniel Tschinder 2017-11-01 16:17:41 +01:00
commit 52537410ef
7199 changed files with 391014 additions and 0 deletions

View File

@ -0,0 +1,2 @@
test/fixtures
lib

31
packages/babylon/.babelrc Normal file
View File

@ -0,0 +1,31 @@
{
"presets": [
["env", {
"loose": true,
"modules": false,
"targets": {
"node": "4.2"
}
}],
"flow"
],
"plugins": [
"external-helpers"
],
"env": {
"watch": {
"presets": [
["env", {
"loose": true,
"targets": {
"node": "current"
}
}],
"flow"
]
},
"test": {
"plugins": ["istanbul"]
}
}
}

View File

@ -0,0 +1,11 @@
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
[*.{js,json}]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,7 @@
.nyc_output
build
coverage
lib
node_modules
test/expressions
test/fixtures

View File

@ -0,0 +1,14 @@
{
"root": true,
"extends": "babel",
"plugins": [
"prettier"
],
"rules": {
"prettier/prettier": ["error", { "trailingComma": "all" }],
"no-case-declarations": "error"
},
"env": {
"node": true
}
}

View File

@ -0,0 +1,15 @@
[ignore]
.*/lib/.*
.*/test/.*
.*/build/.*
[include]
[libs]
[options]
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
suppress_type=$FlowFixMe
suppress_type=$FlowSubtype

1
packages/babylon/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text eol=lf

View File

@ -0,0 +1,35 @@
<!--- Provide a general summary of the issue in the title above -->
### Input Code
<!--- If you're describing a bug, please let us know which sample code reproduces your problem -->
```js
var your => (code) => here;
```
### Babylon/Babel Configuration
<!--- If describing a bug, tell us what your babel configuration looks like -->
```js
{
"your": { "config": "here" }
}
```
### Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
### Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
### Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
| software | version
| ---------------- | -------
| Babylon |
| node |
| npm |
| Operating System |

View File

@ -0,0 +1,17 @@
<!---
Before making a PR please make sure to read our contributing guidelines
https://github.com/babel/babylon/blob/master/CONTRIBUTING.md
-->
| Q | A
| ----------------- | ---
| Bug fix? | yes/no
| Breaking change? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
| Spec compliancy? | yes/no
| Tests added/pass? | yes/no
| Fixed tickets | comma-separated list of tickets fixed by the PR, if any
| License | MIT
<!-- Describe your changes below in as much detail as possible -->

7
packages/babylon/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.nyc_output
build
coverage
lib
node_modules
npm-debug.log
.vscode

View File

@ -0,0 +1,58 @@
sudo: false
language: node_js
node_js:
- "8"
- "6"
- "4"
env:
global:
- PATH=$HOME/.yarn/bin:$PATH
- JOB=test
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.28.4
- yarn global add greenkeeper-lockfile@1
before_script:
- greenkeeper-lockfile-update
- 'if [ "$JOB" = "babel-test" ]; then make bootstrap-babel ; fi'
- 'if [ "$JOB" = "flow-test" ]; then make bootstrap-flow ; fi'
- 'if [ "$JOB" = "test262-test" ]; then make bootstrap-test262 ; fi'
- 'if [ "$JOB" = "test" ]; then yarn run build; fi'
script:
- 'if [ "$JOB" = "test" ]; then yarn test-only; fi'
- 'if [ "$JOB" = "lint" ]; then yarn run lint && yarn run flow; fi'
- 'if [ "$JOB" = "flow-test" ]; then make test-flow; fi'
- 'if [ "$JOB" = "babel-test" ]; then make test-babel; fi'
- 'if [ "$JOB" = "test262-test" ]; then make test-test262; fi'
- 'if [ "$JOB" = "test-coverage" ]; then yarn run test-coverage; fi'
matrix:
fast_finish: true
include:
- node_js: "lts/*"
env: JOB=test-coverage
- node_js: "lts/*"
env: JOB=lint
- node_js: "lts/*"
env: JOB=babel-test
- node_js: "lts/*"
env: JOB=flow-test
- node_js: node
env: JOB=test262-test
after_script:
- greenkeeper-lockfile-upload
after_success:
- 'if [ "$JOB" = "babel-test" ]; then bash <(curl -s https://codecov.io/bash) -f coverage/coverage-final.json -F babel ; fi'
- 'if [ "$JOB" = "test-coverage" ]; then bash <(curl -s https://codecov.io/bash) -f coverage/coverage-final.json -F babylon ; fi'
notifications:
slack: babeljs:5Wy4QX13KVkGy9CnU0rmvgeK
branches:
except:
- /^v\d+\.\d+\.\d+$/

1
packages/babylon/.yarnrc Normal file
View File

@ -0,0 +1 @@
workspaces-experimental true

41
packages/babylon/AUTHORS Normal file
View File

@ -0,0 +1,41 @@
List of Acorn contributors. Updated before every release.
Adrian Rakovsky
Alistair Braidwood
Andres Suarez
Aparajita Fishman
Arian Stolwijk
Artem Govorov
Brandon Mills
Charles Hughes
Conrad Irwin
David Bonnet
Forbes Lindesay
Gilad Peleg
impinball
Ingvar Stepanyan
Jesse McCarthy
Jiaxing Wang
Joel Kemp
Johannes Herr
Jürg Lehni
keeyipchan
Kevin Kwok
krator
Marijn Haverbeke
Martin Carlberg
Mathias Bynens
Mathieu 'p01' Henri
Max Schaefer
Max Zerzouri
Mihai Bazon
Mike Rennie
Nick Fitzgerald
Oskar Schöldström
Paul Harper
Peter Rust
PlNG
r-e-d
Rich Harris
Sebastian McKenzie
zsjforcn

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)

View File

@ -0,0 +1,138 @@
# Contributing
Contributions are always welcome, no matter how large or small. Before
contributing, please read the
[code of conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md).
## Resources
Check out the [babel-handbook](https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md#asts) and [ASTExplorer](http://astexplorer.net/) for some info about contributing to this project regarding Abstract Syntax Trees (ASTs).
## Setup local env
> Install yarn beforehand: https://yarnpkg.com/en/docs/install
To start developing on Babylon you only need to install its dependencies:
```bash
git clone https://github.com/babel/babylon
cd babylon
yarn
```
## Tests
### Running tests locally
To run a build, tests and perform lint/flow checks:
```bash
yarn test
```
If you only want to run the tests:
```bash
yarn run test-only
```
Note, this does not actually run a build, so you may have to call `yarn run build` after
performing any changes.
### Running one test
To run only a single test, add `"only": true` to the `options.json` inside any test fixture folder (you may have to create the file if it doesn't exist).
For example, let's say we want to only run the test for the [`test/fixtures/comments/basic/shebang-import`](https://github.com/babel/babylon/tree/7.0/test/fixtures/comments/basic/shebang-import) fixture.
Add `"only": true` to its `options.json`:
```json
{
"sourceType": "module",
"only": true
}
```
Then, run the tests using the same command as before:
```bash
yarn run test-only
```
### Checking code coverage locally
To generate code coverage, be sure to set `BABEL_ENV=test` so that code is instrumented during
the rollup build.
```bash
BABEL_ENV=test yarn run build && yarn run test-coverage
```
### Writing tests
Writing tests for Babylon is very
[similar to Babel](https://github.com/babel/babel/blob/master/CONTRIBUTING.md#writing-tests).
Inside the `tests/fixtures` folder are categories/groupings of test fixtures (es2015, flow,
etc.). To add a test, create a folder under one of these groupings (or create a new one) with a
descriptive name, and add the following:
* Create an `actual.js` file that contains the code you want Babylon to parse.
* Add an `expected.json` file with the expected parser output. For added convenience, if
there is no `expected.json` present, the test runner will generate one for you.
## Cross repository changes
If you are making changes to Babylon which make it necessary to also change things in Babel
you will want to link both repositories together. This can be done by doing the following
(assuming you have both Babel and Babylon already checked out):
```bash
cd babylon/
yarn link
yarn run build
cd ../babel/
make bootstrap
yarn link babylon
cd packages/babel-core/
yarn link babylon
cd ../babel-template/
yarn link babylon
cd ../babel-traverse/
yarn link babylon
cd ../babel-generator/
yarn link babylon
cd ../babel-types/
yarn link babylon
cd ../..
make build
make test
```
From now on Babel will use your local checkout of Babylon for its tests.
## Creating a new plugin (`spec-new`)
> Example: https://github.com/babel/babylon/pull/541
- Create a new issue that describes the proposal (ex: [#538](https://github.com/babel/babylon/issues/538)). Include any relevant information like proposal repo/author, examples, parsing approaches, meeting notes, presentation slides, and more.
- The pull request should include:
- [ ] An update to the [#plugins](https://github.com/babel/babylon#plugins) part of the readme. Add a new entry to that list for the new plugin flag (and link to the proposal)
- [ ] If any new nodes or modifications need to be added to the AST, update [ast/spec.md](https://github.com/babel/babylon/blob/master/ast/spec.md)
- [ ] Make sure you use the `this.hasPlugin("plugin-name-here")` check so that your new plugin code only runs when that flag is turned on (not default behavior)
- [ ] Add failing/passing tests according to spec behavior
- [ ] Start working about the Babel transform itself!
## Publishing
```sh
# run lerna-changelog from global install and copy it
lerna-changelog
# create tag or specific one (ex: `npm version v7.0.0-beta.16`)
npm version patch
# push to github
git push --follow-tags
# publish (sometimes need `npm publish --tag=next`)
npm publish
# upload changelog to github releases, etc
```

19
packages/babylon/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

47
packages/babylon/Makefile Normal file
View File

@ -0,0 +1,47 @@
MAKEFLAGS = -j1
TEST262_COMMIT = d9f62e4ccf11b2248c2045fb980bf4255621bab0
export NODE_ENV = test
# Fix color output until TravisCI fixes https://github.com/travis-ci/travis-ci/issues/7967
export FORCE_COLOR = true
.PHONY: make clean test test-only test-cov test-clean test-travis publish build bootstrap publish-core publish-runtime build-website build-core watch-core build-core-test clean-core prepublish
clean: ; rm -rf ./build
bootstrap-babel: clean
mkdir ./build
git clone --depth=1 --branch=master https://github.com/babel/babel.git ./build/babel
cd ./build/babel; \
make bootstrap
find ./build/babel/packages -type d -name 'babylon' -prune -exec rm -rf '{}' \; -exec ln -s '../../../../../' '{}' \;
test-babel:
BABEL_ENV=test yarn run build
# in case babel ever switches to nyc: filter its config out of package.json
cd ./build/babel; \
jq "del(.nyc)" package.json > package.nonyc.json; \
mv -f package.nonyc.json package.json; \
../../node_modules/.bin/nyc --no-instrument --no-source-map --reporter=json --report-dir ../../coverage node_modules/mocha/bin/_mocha `scripts/_get-test-directories.sh` --opts test/mocha.opts --compilers js:babel-register; \
bootstrap-flow: clean
mkdir ./build
git clone --depth=1 --branch=master https://github.com/facebook/flow.git ./build/flow
test-flow:
node scripts/run_flow_tests.js
test-flow-update-whitelist:
node scripts/run_flow_tests.js --update-whitelist
bootstrap-test262: clean
mkdir ./build
git clone https://github.com/tc39/test262.git ./build/test262
cd build/test262 && git checkout $(TEST262_COMMIT)
test-test262:
node scripts/run_test262.js
test-test262-update-whitelist:
node scripts/run_test262.js --update-whitelist

173
packages/babylon/README.md Normal file
View File

@ -0,0 +1,173 @@
<p align="center">
<img alt="babylon" src="https://raw.githubusercontent.com/babel/logo/master/babylon.png" width="700">
</p>
<p align="center">
Babylon is a JavaScript parser used in <a href="https://github.com/babel/babel">Babel</a>.
</p>
<p align="center">
<a href="https://www.npmjs.com/package/babylon"><img alt="NPM Version" src="https://img.shields.io/npm/v/babylon.svg?style=flat"></a>
<a href="https://travis-ci.org/babel/babylon"><img alt="Travis Status" src="https://img.shields.io/travis/babel/babylon/master.svg?style=flat&label=travis"></a>
<a href="https://codecov.io/gh/babel/babylon"><img alt="Codecov Status" src="https://img.shields.io/codecov/c/github/babel/babylon/master.svg?style=flat"></a>
</p>
- The latest ECMAScript version enabled by default (ES2017).
- Comment attachment.
- Support for JSX, Flow, Typescript.
- Support for experimental language proposals (accepting PRs for anything at least [stage-0](https://github.com/tc39/proposals/blob/master/stage-0-proposals.md)).
## Contributing
Check out [contributing.md](https://github.com/babel/babylon/blob/master/CONTRIBUTING.md)
## Credits
Heavily based on [acorn](https://github.com/marijnh/acorn) and [acorn-jsx](https://github.com/RReverser/acorn-jsx),
thanks to the awesome work of [@RReverser](https://github.com/RReverser) and [@marijnh](https://github.com/marijnh).
## API
### `babylon.parse(code, [options])`
### `babylon.parseExpression(code, [options])`
`parse()` parses the provided `code` as an entire ECMAScript program, while
`parseExpression()` tries to parse a single Expression with performance in
mind. When in doubt, use `.parse()`.
### Options
- **allowImportExportEverywhere**: By default, `import` and `export`
declarations can only appear at a program's top level. Setting this
option to `true` allows them anywhere where a statement is allowed.
- **allowReturnOutsideFunction**: By default, a return statement at
the top level raises an error. Set this to `true` to accept such
code.
- **allowSuperOutsideMethod**: TODO
- **sourceType**: Indicate the mode the code should be parsed in. Can be
either `"script"` or `"module"`.
- **sourceFilename**: Correlate output AST nodes with their source filename. Useful when generating code and source maps from the ASTs of multiple input files.
- **startLine**: By default, the first line of code parsed is treated as line 1. You can provide a line number to alternatively start with. Useful for integration with other source tools.
- **plugins**: Array containing the plugins that you want to enable.
- **strictMode**: TODO
- **ranges**: Adds a `ranges` property to each node: `[node.start, node.end]`
- **tokens**: Adds all parsed tokens to a `tokens` property on the `File` node
### Output
Babylon generates AST according to [Babel AST format][].
It is based on [ESTree spec][] with the following deviations:
> There is now an `estree` plugin which reverts these deviations
- [Literal][] token is replaced with [StringLiteral][], [NumericLiteral][], [BooleanLiteral][], [NullLiteral][], [RegExpLiteral][]
- [Property][] token is replaced with [ObjectProperty][] and [ObjectMethod][]
- [MethodDefinition][] is replaced with [ClassMethod][]
- [Program][] and [BlockStatement][] contain additional `directives` field with [Directive][] and [DirectiveLiteral][]
- [ClassMethod][], [ObjectProperty][], and [ObjectMethod][] value property's properties in [FunctionExpression][] is coerced/brought into the main method node.
AST for JSX code is based on [Facebook JSX AST][] with the addition of one node type:
- `JSXText`
[Babel AST format]: https://github.com/babel/babylon/blob/master/ast/spec.md
[ESTree spec]: https://github.com/estree/estree
[Literal]: https://github.com/estree/estree/blob/master/es5.md#literal
[Property]: https://github.com/estree/estree/blob/master/es5.md#property
[MethodDefinition]: https://github.com/estree/estree/blob/master/es2015.md#methoddefinition
[StringLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#stringliteral
[NumericLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#numericliteral
[BooleanLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#booleanliteral
[NullLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#nullliteral
[RegExpLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#regexpliteral
[ObjectProperty]: https://github.com/babel/babylon/blob/master/ast/spec.md#objectproperty
[ObjectMethod]: https://github.com/babel/babylon/blob/master/ast/spec.md#objectmethod
[ClassMethod]: https://github.com/babel/babylon/blob/master/ast/spec.md#classmethod
[Program]: https://github.com/babel/babylon/blob/master/ast/spec.md#programs
[BlockStatement]: https://github.com/babel/babylon/blob/master/ast/spec.md#blockstatement
[Directive]: https://github.com/babel/babylon/blob/master/ast/spec.md#directive
[DirectiveLiteral]: https://github.com/babel/babylon/blob/master/ast/spec.md#directiveliteral
[FunctionExpression]: https://github.com/babel/babylon/blob/master/ast/spec.md#functionexpression
[Facebook JSX AST]: https://github.com/facebook/jsx/blob/master/AST.md
### Semver
Babylon follows semver in most situations. The only thing to note is that some spec-compliancy bug fixes may be released under patch versions.
For example: We push a fix to early error on something like [#107](https://github.com/babel/babylon/pull/107) - multiple default exports per file. That would be considered a bug fix even though it would cause a build to fail.
### Example
```javascript
require("babylon").parse("code", {
// parse in strict mode and allow module declarations
sourceType: "module",
plugins: [
// enable jsx and flow syntax
"jsx",
"flow"
]
});
```
### Plugins
| Name | Code Example |
|------|--------------|
| `estree` ([repo](https://github.com/estree/estree)) | n/a |
| `jsx` ([repo](https://facebook.github.io/jsx/)) | `<a attr="b">{s}</a>` |
| `flow` ([repo](https://github.com/facebook/flow)) | `var a: string = "";` |
| `typescript` ([repo](https://github.com/Microsoft/TypeScript)) | `var a: string = "";` |
| `doExpressions` | `var a = do { if (true) { 'hi'; } };` |
| `objectRestSpread` ([proposal](https://github.com/tc39/proposal-object-rest-spread)) | `var a = { b, ...c };` |
| `decorators` (Stage 1) and `decorators2` (Stage 2 [proposal](https://github.com/tc39/proposal-decorators)) | `@a class A {}` |
| `classProperties` ([proposal](https://github.com/tc39/proposal-class-public-fields)) | `class A { b = 1; }` |
| `classPrivateProperties` ([proposal](https://github.com/tc39/proposal-private-fields)) | `class A { #b = 1; }` |
| `classPrivateMethods` ([proposal](https://github.com/tc39/proposal-private-methods)) | `class A { #c() {} }` |
| `exportExtensions` ([proposal 1](https://github.com/leebyron/ecmascript-export-default-from)), ([proposal 2](https://github.com/leebyron/ecmascript-export-ns-from)) | Proposal 1: `export v from "mod"` Proposal 2: `export * as ns from "mod"` |
| `asyncGenerators` ([proposal](https://github.com/tc39/proposal-async-iteration)) | `async function*() {}`, `for await (let a of b) {}` |
| `functionBind` ([proposal](https://github.com/zenparsing/es-function-bind)) | `a::b`, `::console.log` |
| `functionSent` | `function.sent` |
| `dynamicImport` ([proposal](https://github.com/tc39/proposal-dynamic-import)) | `import('./guy').then(a)` |
| `numericSeparator` ([proposal](https://github.com/samuelgoto/proposal-numeric-separator)) | `1_000_000` |
| `optionalChaining` ([proposal](https://github.com/tc39/proposal-optional-chaining)) | `a?.b` |
| `importMeta` ([proposal](https://github.com/tc39/proposal-import-meta)) | `import.meta.url` |
| `bigInt` ([proposal](https://github.com/tc39/proposal-bigint)) | `100n` |
| `optionalCatchBinding` ([proposal](https://github.com/babel/proposals/issues/7)) | `try {throw 0;} catch{do();}` |
| `throwExpressions` ([proposal](https://github.com/babel/proposals/issues/23)) | `() => throw new Error("")` |
| `pipelineOperator` ([proposal](https://github.com/babel/proposals/issues/29)) | `a \|> b` |
| `nullishCoalescingOperator` ([proposal](https://github.com/babel/proposals/issues/14)) | `a ?? b` |
### FAQ
#### Will Babylon support a plugin system?
Previous issues: [babel/babel#1351](https://github.com/babel/babel/issues/1351), [#500](https://github.com/babel/babylon/issues/500).
We currently aren't willing to commit to supporting the API for plugins or the resulting ecosystem (there is already enough work maintaining Babel's own plugin system). It's not clear how to make that API effective, and it would limit out ability to refactor and optimize the codebase.
Our current recommendation for those that want to create their own custom syntax is for users to fork Babylon.
To consume your custom parser, you can add to your `.babelrc` via its npm package name or require it if using JavaScript,
```json
{
"parserOpts": {
"parser": "custom-fork-of-babylon-on-npm-here"
}
}
```

View File

View File

1237
packages/babylon/ast/spec.md Normal file

File diff suppressed because it is too large Load Diff

16
packages/babylon/bin/babylon.js Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env node
/* eslint no-var: 0 */
var babylon = require("..");
var fs = require("fs");
var filename = process.argv[2];
if (!filename) {
console.error("no filename specified");
process.exit(0);
}
var file = fs.readFileSync(filename, "utf8");
var ast = babylon.parse(file);
console.log(JSON.stringify(ast, null, " "));

View File

@ -0,0 +1,71 @@
"use strict";
// Which Unicode version should be used?
const version = "9.0.0";
const start = require("unicode-" +
version +
"/Binary_Property/ID_Start/code-points.js").filter(function(ch) {
return ch > 0x7f;
});
let last = -1;
const cont = [0x200c, 0x200d].concat(
require("unicode-" +
version +
"/Binary_Property/ID_Continue/code-points.js").filter(function(ch) {
return ch > 0x7f && search(start, ch, last + 1) == -1;
}),
);
function search(arr, ch, starting) {
for (let i = starting; arr[i] <= ch && i < arr.length; last = i++)
if (arr[i] === ch) return i;
return -1;
}
function pad(str, width) {
while (str.length < width) str = "0" + str;
return str;
}
function esc(code) {
const hex = code.toString(16);
if (hex.length <= 2) return "\\x" + pad(hex, 2);
else return "\\u" + pad(hex, 4);
}
function generate(chars) {
const astral = [];
let re = "";
for (let i = 0, at = 0x10000; i < chars.length; i++) {
const from = chars[i];
let to = from;
while (i < chars.length - 1 && chars[i + 1] == to + 1) {
i++;
to++;
}
if (to <= 0xffff) {
if (from == to) re += esc(from);
else if (from + 1 == to) re += esc(from) + esc(to);
else re += esc(from) + "-" + esc(to);
} else {
astral.push(from - at, to - from);
at = to;
}
}
return { nonASCII: re, astral: astral };
}
const startData = generate(start);
const contData = generate(cont);
console.log('let nonASCIIidentifierStartChars = "' + startData.nonASCII + '";');
console.log('let nonASCIIidentifierChars = "' + contData.nonASCII + '";');
console.log(
"const astralIdentifierStartCodes = " +
JSON.stringify(startData.astral) +
";",
);
console.log(
"const astralIdentifierCodes = " + JSON.stringify(contData.astral) + ";",
);

View File

@ -0,0 +1,4 @@
coverage:
parsers:
javascript:
enable_partials: yes

View File

@ -0,0 +1,102 @@
{
"name": "babylon",
"version": "7.0.0-beta.30",
"description": "A JavaScript parser",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"keywords": [
"babel",
"javascript",
"parser",
"babylon"
],
"repository": "https://github.com/babel/babylon",
"main": "lib/index.js",
"files": [
"bin",
"lib"
],
"engines": {
"node": ">=4.2.0"
},
"devDependencies": {
"ava": "^0.22.0",
"babel-core": "7.0.0-alpha.18",
"babel-eslint": "8.0.1",
"babel-helper-fixtures": "7.0.0-alpha.18",
"babel-plugin-external-helpers": "7.0.0-alpha.18",
"babel-plugin-istanbul": "^4.1.4",
"babel-preset-env": "2.0.0-alpha.18",
"babel-preset-flow": "7.0.0-alpha.18",
"chalk": "^2.0.0",
"cross-env": "^5.0.0",
"eslint": "^4.0.0",
"eslint-config-babel": "^7.0.1",
"eslint-plugin-flowtype": "^2.34.0",
"eslint-plugin-prettier": "^2.1.2",
"flow-bin": "^0.56.0",
"graceful-fs": "^4.1.11",
"husky": "^0.14.1",
"lint-staged": "^4.0.0",
"nyc": "^11.0.3",
"prettier": "^1.5.2",
"rimraf": "^2.5.4",
"rollup": "^0.50.0",
"rollup-plugin-babel": "^3.0.1",
"rollup-plugin-node-resolve": "^3.0.0",
"rollup-watch": "^4.0.0",
"unicode-9.0.0": "~0.7.0",
"util.promisify": "^1.0.0"
},
"bin": {
"babylon": "./bin/babylon.js"
},
"scripts": {
"build": "yarn run clean && rollup -c",
"changelog": "git log `git describe --tags --abbrev=0`..HEAD --pretty=format:' * %s (%an)' | grep -v 'Merge pull request'",
"clean": "rimraf lib",
"flow": "flow",
"lint": "eslint src bin test",
"prepublish": "cross-env BABEL_ENV=production yarn run build",
"preversion": "yarn run test && npm run changelog",
"test": "yarn run lint && yarn run flow && yarn run build -- -m && yarn run test-only",
"test-only": "ava",
"test-coverage": "cross-env BABEL_ENV=test yarn run build && nyc --reporter=json --reporter=text yarn run test-only",
"watch": "yarn run clean && rollup -c --watch"
},
"nyc": {
"include": [
"src/**/*.js",
"bin/**/*.js"
],
"sourceMap": false,
"instrument": false
},
"ava": {
"files": [
"test/*.js"
],
"source": [
"src/**/*.js",
"bin/**/*.js"
]
},
"changelog": {
"repo": "babel/babylon",
"labels": {
"Tag: Breaking Change": ":boom: Breaking Change",
"Tag: Bug Fix": ":bug: Bug Fix",
"Tag: Docs": ":memo: Documentation",
"Tag: Internal": ":house: Internal",
"Tag: New Feature": ":rocket: New Feature",
"Tag: Polish": ":nail_care: Polish"
}
},
"lint-staged": {
"*.js": [
"eslint --format=codeframe --fix",
"git add"
]
}
}

View File

@ -0,0 +1,11 @@
import babel from "rollup-plugin-babel";
import nodeResolve from "rollup-plugin-node-resolve";
export default {
input: "src/index.js",
output: {
file: "lib/index.js",
format: "cjs",
},
plugins: [babel(), nodeResolve()],
};

View File

@ -0,0 +1,60 @@
# This file lists tests that are known to produce incorrect results when parsed
# with Babylon:
#
# - Tests that are expected to parse successfully but for which Babylon reports
# a syntax error
# - Tests that contain invalid syntax but for which Babylon reports no syntax
# error
#
# Entries should be removed incrementally as Babylon is improved.
ES6/binding-pattern/object-pattern/await-prop-in-async-function.js
JSX_invalid/migrated_0000.js
arrow_function_invalid/migrated_0002.js
async_await/async_generic_method.js
async_await/migrated_0020.js
async_await/migrated_0024.js
async_await/migrated_0027.js
async_generators/migrated_0007.js
class_properties/migrated_0000.js
class_properties/migrated_0005.js
class_properties/migrated_0011.js
class_properties/migrated_0016.js
class_properties/migrated_0021.js
class_properties/migrated_0026.js
decorators/migrated_0003.js
decorators/migrated_0007.js
dynamic_import/migrated_0000.js
dynamic_import/migrated_0001.js
dynamic_import/migrated_0002.js
dynamic_import/migrated_0003.js
dynamic_import/migrated_0004.js
invalid_syntax/migrated_0000.js
invalid_syntax/migrated_0001.js
invalid_syntax/migrated_0002.js
invalid_syntax/migrated_0003.js
invalid_syntax/migrated_0010.js
private_class_properties/valid.js
types/annotations/migrated_0001.js
types/annotations_in_comments_invalid/migrated_0000.js
types/annotations_in_comments_invalid/migrated_0001.js
types/annotations_in_comments_invalid/migrated_0002.js
types/annotations_in_comments_invalid/migrated_0003.js
types/annotations_in_comments_invalid/migrated_0004.js
types/annotations/static_is_reserved_param.js
types/annotations/static_is_reserved_type.js
types/annotations/void_is_reserved_param.js
types/declare_export_invalid/migrated_0013.js
types/declare_statements_invalid/migrated_0001.js
types/member/reserved_words.js
types/number_literal_invalid/migrated_0000.js
types/parameter_defaults/migrated_0023.js
types/parameter_defaults/migrated_0026.js
types/parameter_defaults/migrated_0028.js
types/parameter_defaults/migrated_0029.js
types/parameter_defaults/migrated_0030.js
types/parameter_defaults/migrated_0031.js
types/parameter_defaults/migrated_0032.js
types/string_literal_invalid/migrated_0000.js
types/typecasts_invalid/migrated_0001.js
types/import_types/typeof_named_reserved_type.js

View File

@ -0,0 +1,24 @@
// Use this to remove all "expected.json" in all tests.
const { existsSync, readdirSync, statSync, unlinkSync } = require("fs");
const { join } = require("path");
const rootPath = join(__dirname, "..", "test", "fixtures");
for (const fixtureName of readdirSync(rootPath)) {
const fixturePath = join(rootPath, fixtureName);
for (const suiteName of readdirSync(fixturePath)) {
const suitePath = join(fixturePath, suiteName);
if (!statSync(suitePath).isDirectory()) {
continue;
}
for (const testName of readdirSync(suitePath)) {
const testPath = join(suitePath, testName);
const expectedPath = join(testPath, "expected.json");
if (existsSync(expectedPath)) {
unlinkSync(expectedPath);
}
}
}
}

View File

@ -0,0 +1,272 @@
"use strict";
const path = require("path");
const fs = require("fs");
const chalk = require("chalk");
const parse = require("..").parse;
const TESTS_FOLDER = path.join(__dirname, "../build/flow/src/parser/test/flow");
const WHITELIST_PATH = path.join(__dirname, "./flow_tests_whitelist.txt");
const shouldUpdateWhitelist = process.argv.indexOf("--update-whitelist") > 0;
function map_get_default(map, key, defaultConstructor) {
if (map.has(key)) {
return map.get(key);
}
const value = new defaultConstructor();
map.set(key, value);
return value;
}
function get_whitelist(filename) {
return fs
.readFileSync(filename, "utf8")
.split("\n")
.map(line => line.replace(/#.*$/, "").trim())
.filter(Boolean);
}
function list_files(root, dir) {
const files = fs.readdirSync(dir ? path.join(root, dir) : root);
let result = [];
for (let i = 0; i < files.length; i++) {
const file = dir ? path.join(dir, files[i]) : files[i];
const stats = fs.statSync(path.join(root, file));
if (stats.isDirectory()) {
result = result.concat(list_files(root, file));
} else {
result.push(file);
}
}
return result.sort();
}
function get_tests(root_dir) {
const files = list_files(root_dir);
const tests = new Map();
for (let i = 0; i < files.length; i++) {
const file = files[i];
const test_name = path.dirname(file);
const case_parts = path.basename(file).split(".");
const case_name = case_parts[0];
// Hack to ignore hidden files.
if (case_name === "") {
continue;
}
const cases = map_get_default(tests, test_name, Map);
const case_ = map_get_default(cases, case_name, Object);
const content = fs.readFileSync(path.join(root_dir, file), "utf8");
const ext = case_parts[case_parts.length - 1];
const kind =
case_parts.length > 2 ? case_parts[case_parts.length - 2] : null;
if (ext === "js") {
case_.file = file;
case_.content = content;
} else if (ext === "json" && kind === "tree") {
case_.expected_ast = JSON.parse(content);
} else if (ext === "json" && kind === "options") {
case_.options = JSON.parse(content);
}
}
return tests;
}
function update_whitelist(summary) {
const contains = (tests, file) =>
tests.some(({ test }) => test.file === file);
const result = fs
.readFileSync(WHITELIST_PATH, "utf8")
.split("\n")
.filter(line => {
const file = line.replace(/#.*$/, "").trim();
return (
!contains(summary.disallowed.success, file) &&
!contains(summary.disallowed.failure, file) &&
summary.unrecognized.indexOf(file) === -1
);
})
.join("\n");
fs.writeFileSync(WHITELIST_PATH, result);
}
const options = {
plugins: ["jsx", "flow", "asyncGenerators", "objectRestSpread"],
sourceType: "module",
ranges: true,
};
const flowOptionsMapping = {
esproposal_class_instance_fields: "classProperties",
esproposal_class_static_fields: "classProperties",
esproposal_export_star_as: "exportExtensions",
esproposal_decorators: "decorators",
};
const summary = {
passed: true,
allowed: {
success: [],
failure: [],
},
disallowed: {
success: [],
failure: [],
},
unrecognized: []
};
const tests = get_tests(TESTS_FOLDER);
const whitelist = get_whitelist(WHITELIST_PATH);
const unrecognized = new Set(whitelist);
tests.forEach(section => {
section.forEach(test => {
const shouldSuccess =
test.expected_ast &&
(!Array.isArray(test.expected_ast.errors) ||
test.expected_ast.errors.length === 0);
const inWhitelist = whitelist.indexOf(test.file) > -1;
const babylonOptions = Object.assign({}, options);
babylonOptions.plugins = babylonOptions.plugins.slice();
if (test.options) {
Object.keys(test.options).forEach(option => {
if (!test.options[option]) return;
if (!flowOptionsMapping[option])
throw new Error("Parser options not mapped " + option);
babylonOptions.plugins.push(flowOptionsMapping[option]);
});
}
let failed = false;
let exception = null;
try {
parse(test.content, babylonOptions);
} catch (e) {
exception = e;
failed = true;
// lets retry in script mode
try {
parse(
test.content,
Object.assign({}, babylonOptions, { sourceType: "script" })
);
exception = null;
failed = false;
} catch (e) {}
}
const isSuccess = shouldSuccess !== failed;
const isAllowed = isSuccess !== inWhitelist;
summary[isAllowed ? "allowed" : "disallowed"][
isSuccess ? "success" : "failure"
].push({ test, exception, shouldSuccess, babylonOptions });
summary.passed &= isAllowed;
unrecognized.delete(test.file);
process.stdout.write(chalk.gray("."));
});
});
summary.unrecognized = Array.from(unrecognized);
summary.passed &= summary.unrecognized.length === 0;
// This is needed because, after the dots written using
// `process.stdout.write(".")` there is no final newline
console.log();
if (summary.disallowed.failure.length || summary.disallowed.success.length) {
console.log("\n-- FAILED TESTS --");
summary.disallowed.failure.forEach(
({ test, shouldSuccess, exception, babylonOptions }) => {
console.log(chalk.red(`${test.file}`));
if (shouldSuccess) {
console.log(chalk.yellow(" Should parse successfully, but did not"));
console.log(chalk.yellow(` Failed with: \`${exception.message}\``));
} else {
console.log(chalk.yellow(" Should fail parsing, but did not"));
}
console.log(
chalk.yellow(
` Active plugins: ${JSON.stringify(babylonOptions.plugins)}`
)
);
}
);
summary.disallowed.success.forEach(
({ test, shouldSuccess, babylonOptions }) => {
console.log(chalk.red(`${test.file}`));
if (shouldSuccess) {
console.log(
chalk.yellow(
" Correctly parsed successfully, but" +
" was disallowed by the whitelist"
)
);
} else {
console.log(
chalk.yellow(
" Correctly failed parsing, but" +
" was disallowed by the whitelist"
)
);
}
console.log(
chalk.yellow(
` Active plugins: ${JSON.stringify(babylonOptions.plugins)}`
)
);
}
);
}
console.log("-- SUMMARY --");
console.log(
chalk.green("✔ " + summary.allowed.success.length + " tests passed")
);
console.log(
chalk.green(
"✔ " +
summary.allowed.failure.length +
" tests failed but were allowed in the whitelist"
)
);
console.log(
chalk.red("✘ " + summary.disallowed.failure.length + " tests failed")
);
console.log(
chalk.red(
"✘ " +
summary.disallowed.success.length +
" tests passed but were disallowed in the whitelist"
)
);
console.log(
chalk.red(
"✘ " +
summary.unrecognized.length +
" tests specified in the whitelist were not found"
)
);
// Some padding to separate the output from the message `make`
// adds at the end of failing scripts
console.log();
if (shouldUpdateWhitelist) {
update_whitelist(summary);
console.log("\nWhitelist updated");
} else {
process.exit(summary.passed ? 0 : 1);
}

View File

@ -0,0 +1,113 @@
"use strict";
const path = require("path");
const chalk = require("chalk");
const utils = require("./run_test262_utils");
const testDir = path.join(__dirname, "..", "build", "test262", "test");
const whitelistFile = path.join(__dirname, "test262_whitelist.txt");
const plugins = ["asyncGenerators", "objectRestSpread", "optionalCatchBinding"];
const shouldUpdate = process.argv.indexOf("--update-whitelist") > -1;
Promise.all([utils.getTests(testDir), utils.getWhitelist(whitelistFile)])
.then(function([tests, whitelist]) {
const total = tests.length;
const reportInc = Math.floor(total / 20);
console.log(`Now running ${total} tests...`);
const results = tests.map(function(test, idx) {
if (idx % reportInc === 0) {
console.log(`> ${Math.round(100 * idx / total)}% complete`);
}
return utils.runTest(test, plugins);
});
return utils.interpret(results, whitelist);
})
.then(function(summary) {
const goodnews = [
summary.allowed.success.length + " valid programs parsed without error",
summary.allowed.failure.length +
" invalid programs produced a parsing error",
summary.allowed.falsePositive.length +
" invalid programs did not produce a parsing error" +
" (and allowed by the whitelist file)",
summary.allowed.falseNegative.length +
" valid programs produced a parsing error" +
" (and allowed by the whitelist file)",
];
const badnews = [];
const badnewsDetails = [];
void [
{
tests: summary.disallowed.success,
label:
"valid programs parsed without error" +
" (in violation of the whitelist file)",
},
{
tests: summary.disallowed.failure,
label:
"invalid programs produced a parsing error" +
" (in violation of the whitelist file)",
},
{
tests: summary.disallowed.falsePositive,
label:
"invalid programs did not produce a parsing error" +
" (without a corresponding entry in the whitelist file)",
},
{
tests: summary.disallowed.falseNegative,
label:
"valid programs produced a parsing error" +
" (without a corresponding entry in the whitelist file)",
},
{
tests: summary.unrecognized,
label: "non-existent programs specified in the whitelist file",
},
].forEach(function({ tests, label }) {
if (!tests.length) {
return;
}
const desc = tests.length + " " + label;
badnews.push(desc);
badnewsDetails.push(desc + ":");
badnewsDetails.push(
...tests.map(function(test) {
return test.id || test;
})
);
});
console.log("Testing complete.");
console.log("Summary:");
console.log(chalk.green(goodnews.join("\n").replace(/^/gm, " ✔ ")));
if (!summary.passed) {
console.log("");
console.log(chalk.red(badnews.join("\n").replace(/^/gm, " ✘ ")));
console.log("");
console.log("Details:");
console.log(badnewsDetails.join("\n").replace(/^/gm, " "));
}
if (shouldUpdate) {
return utils.updateWhitelist(whitelistFile, summary).then(function() {
console.log("");
console.log("Whitelist file updated.");
});
} else {
process.exitCode = summary.passed ? 0 : 1;
}
})
.catch(function(err) {
console.error(err);
process.exitCode = 1;
});

View File

@ -0,0 +1,233 @@
"use strict";
const fs = require("graceful-fs");
const path = require("path");
const promisify = require("util.promisify");
const pfs = {
readFile: promisify(fs.readFile),
writeFile: promisify(fs.writeFile),
readdir: promisify(fs.readdir),
stat: promisify(fs.stat)
};
const parse = require("..").parse;
const modulePattern = /^\s*-\s*module\s*$|^\s*flags\s*:.*\bmodule\b/m;
const noStrictPattern = /^\s*-\s*noStrict\s*$|^\s*flags\s*:.*\bnoStrict\b/m;
const onlyStrictPattern = /^\s*-\s*onlyStrict\s*$|^\s*flags\s*:.*\bonlyStrict\b/m;
const rawPattern = /^\s*-\s*raw\s*$|^\s*flags\s*:.*\braw\b/m;
const testNamePattern = /^(?!.*_FIXTURE).*\.[jJ][sS]$/;
function flatten(array) {
const flattened = [];
array.forEach(function(element) {
if (Array.isArray(element)) {
flattened.push.apply(flattened, element);
} else {
flattened.push(element);
}
});
return flattened;
}
function hasEarlyError(src) {
return !!(
src.match(/^\s*negative:\s*$/m) && src.match(/^\s+phase:\s*early\s*$/m)
);
}
function readDirDeep(dirName) {
return pfs.readdir(dirName).then(function(contents) {
return Promise.all(
contents.map(function(name) {
return findTests(path.join(dirName, name));
})
).then(flatten)
});
}
function findTests(name) {
return pfs.stat(name).then(function(stat) {
if (stat.isDirectory()) {
return readDirDeep(name);
}
return name;
});
}
function readTest(fileName, testDir) {
if (!testNamePattern.test(fileName)) {
return Promise.resolve([]);
}
return pfs.readFile(fileName, "utf-8").then(function(contents) {
return makeScenarios(path.relative(testDir, fileName), contents);
});
}
function makeScenarios(fileName, testContent) {
const scenarios = [];
const base = {
fileName: fileName,
isModule: modulePattern.test(testContent),
expectedError: hasEarlyError(testContent),
};
const isNoStrict = noStrictPattern.test(testContent);
const isOnlyStrict = onlyStrictPattern.test(testContent);
const isRaw = rawPattern.test(testContent);
if (!isOnlyStrict) {
scenarios.push(
Object.assign(
{
id: fileName + "(default)",
content: testContent,
},
base
)
);
}
if (!isNoStrict && !isRaw) {
scenarios.push(
Object.assign(
{
id: fileName + "(strict mode)",
content: "'use strict';\n" + testContent,
},
base
)
);
}
return scenarios;
}
exports.getTests = function(testDir) {
return findTests(testDir)
.then(function(testPaths) {
return Promise.all(
testPaths.map(function(path) {
return readTest(path, testDir);
})
);
})
.then(flatten);
};
exports.runTest = function(test, plugins) {
const sourceType = test.isModule ? "module" : "script";
try {
parse(test.content, { sourceType: sourceType, plugins: plugins });
test.actualError = false;
} catch (err) {
test.actualError = true;
}
test.result = test.expectedError !== test.actualError ? "fail" : "pass";
return test;
};
exports.getWhitelist = function(filename) {
return pfs.readFile(filename, "utf-8").then(function(contents) {
return contents
.split("\n")
.map(function(line) {
return line.replace(/#.*$/, "").trim();
})
.filter(function(line) {
return line.length > 0;
})
.reduce(function(table, filename) {
table[filename] = true;
return table;
}, Object.create(null));
});
};
exports.updateWhitelist = function(filename, summary) {
return pfs.readFile(filename, "utf-8").then(function(contents) {
const toRemove = summary.disallowed.success
.concat(summary.disallowed.failure)
.map(function(test) {
return test.id;
});
const toAdd = summary.disallowed.falsePositive
.concat(summary.disallowed.falseNegative)
.map(function(test) {
return test.id;
});
const newContents = contents
.split("\n")
.map(function(line) {
const testId = line.replace(/#.*$/, "").trim();
if (toRemove.indexOf(testId) > -1) {
return null;
}
return line;
})
.filter(function(line) {
return line !== null;
})
.concat(toAdd)
.join("\n");
return pfs.writeFile(filename, newContents, "utf-8");
});
};
exports.interpret = function(results, whitelist) {
const summary = {
passed: true,
allowed: {
success: [],
failure: [],
falsePositive: [],
falseNegative: [],
},
disallowed: {
success: [],
failure: [],
falsePositive: [],
falseNegative: [],
},
unrecognized: null,
};
results.forEach(function(result) {
let classification, isAllowed;
const inWhitelist = result.id in whitelist;
delete whitelist[result.id];
if (!result.expectedError) {
if (!result.actualError) {
classification = "success";
isAllowed = !inWhitelist;
} else {
classification = "falseNegative";
isAllowed = inWhitelist;
}
} else {
if (!result.actualError) {
classification = "falsePositive";
isAllowed = inWhitelist;
} else {
classification = "failure";
isAllowed = !inWhitelist;
}
}
summary.passed &= isAllowed;
summary[isAllowed ? "allowed" : "disallowed"][classification].push(result);
});
summary.unrecognized = Object.keys(whitelist);
summary.passed = !!summary.passed && summary.unrecognized.length === 0;
return summary;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
"use strict";
const exec = require("child_process").exec;
const runIfYarn = fn => {
exec("yarn -V", error => {
if (error === null) fn();
});
};
runIfYarn(() => {
console.log("`package.json` was changed. Running yarn...🐈");
exec("yarn");
});

93
packages/babylon/src/index.js Executable file
View File

@ -0,0 +1,93 @@
// @flow
import type { Options } from "./options";
import Parser, { plugins } from "./parser";
import { types as tokTypes } from "./tokenizer/types";
import "./tokenizer/context";
import type { Expression, File } from "./types";
import estreePlugin from "./plugins/estree";
import flowPlugin from "./plugins/flow";
import jsxPlugin from "./plugins/jsx";
import typescriptPlugin from "./plugins/typescript";
plugins.estree = estreePlugin;
plugins.flow = flowPlugin;
plugins.jsx = jsxPlugin;
plugins.typescript = typescriptPlugin;
export function parse(input: string, options?: Options): File {
return getParser(options, input).parse();
}
export function parseExpression(input: string, options?: Options): Expression {
const parser = getParser(options, input);
if (parser.options.strictMode) {
parser.state.strict = true;
}
return parser.getExpression();
}
export { tokTypes };
function getParser(options: ?Options, input: string): Parser {
const cls =
options && options.plugins ? getParserClass(options.plugins) : Parser;
return new cls(options, input);
}
const parserClassCache: { [key: string]: Class<Parser> } = {};
/** Get a Parser class with plugins applied. */
function getParserClass(
pluginsFromOptions: $ReadOnlyArray<string>,
): Class<Parser> {
if (
pluginsFromOptions.indexOf("decorators") >= 0 &&
pluginsFromOptions.indexOf("decorators2") >= 0
) {
throw new Error("Cannot use decorators and decorators2 plugin together");
}
// Filter out just the plugins that have an actual mixin associated with them.
let pluginList = pluginsFromOptions.filter(
p => p === "estree" || p === "flow" || p === "jsx" || p === "typescript",
);
if (pluginList.indexOf("flow") >= 0) {
// ensure flow plugin loads last
pluginList = pluginList.filter(plugin => plugin !== "flow");
pluginList.push("flow");
}
if (
pluginList.indexOf("flow") >= 0 &&
pluginList.indexOf("typescript") >= 0
) {
throw new Error("Cannot combine flow and typescript plugins.");
}
if (pluginList.indexOf("typescript") >= 0) {
// ensure typescript plugin loads last
pluginList = pluginList.filter(plugin => plugin !== "typescript");
pluginList.push("typescript");
}
if (pluginList.indexOf("estree") >= 0) {
// ensure estree plugin loads first
pluginList = pluginList.filter(plugin => plugin !== "estree");
pluginList.unshift("estree");
}
const key = pluginList.join("/");
let cls = parserClassCache[key];
if (!cls) {
cls = Parser;
for (const plugin of pluginList) {
cls = plugins[plugin](cls);
}
parserClassCache[key] = cls;
}
return cls;
}

60
packages/babylon/src/options.js Executable file
View File

@ -0,0 +1,60 @@
// @flow
// A second optional argument can be given to further configure
// the parser process. These options are recognized:
export type Options = {
sourceType: "script" | "module",
sourceFilename?: string,
startLine: number,
allowReturnOutsideFunction: boolean,
allowImportExportEverywhere: boolean,
allowSuperOutsideMethod: boolean,
plugins: $ReadOnlyArray<string>,
strictMode: ?boolean,
ranges: boolean,
tokens: boolean,
};
export const defaultOptions: Options = {
// Source type ("script" or "module") for different semantics
sourceType: "script",
// Source filename.
sourceFilename: undefined,
// Line from which to start counting source. Useful for
// integration with other tools.
startLine: 1,
// When enabled, a return at the top level is not considered an
// error.
allowReturnOutsideFunction: false,
// When enabled, import/export statements are not constrained to
// appearing at the top of the program.
allowImportExportEverywhere: false,
// TODO
allowSuperOutsideMethod: false,
// An array of plugins to enable
plugins: [],
// TODO
strictMode: null,
// Nodes have their start and end characters offsets recorded in
// `start` and `end` properties (directly on the node, rather than
// the `loc` object, which holds line/column data. To also add a
// [semi-standardized][range] `range` property holding a `[start,
// end]` array with the same numbers, set the `ranges` option to
// `true`.
//
// [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
ranges: false,
// Adds all parsed tokens to a `tokens` property on the `File` node
tokens: false,
};
// Interpret and default an options object
export function getOptions(opts: ?Options): Options {
const options: any = {};
for (const key in defaultOptions) {
options[key] = opts && opts[key] != null ? opts[key] : defaultOptions[key];
}
return options;
}

View File

@ -0,0 +1,30 @@
// @flow
import type { Options } from "../options";
import { reservedWords } from "../util/identifier";
import type State from "../tokenizer/state";
export default class BaseParser {
// Properties set by constructor in index.js
options: Options;
inModule: boolean;
plugins: { [key: string]: boolean };
filename: ?string;
// Initialized by Tokenizer
state: State;
input: string;
isReservedWord(word: string): boolean {
if (word === "await") {
return this.inModule;
} else {
return reservedWords[6](word);
}
}
hasPlugin(name: string): boolean {
return !!this.plugins[name];
}
}

View File

@ -0,0 +1,227 @@
/* eslint max-len: 0 */
// @flow
/**
* Based on the comment attachment algorithm used in espree and estraverse.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import BaseParser from "./base";
import type { Comment, Node } from "../types";
function last<T>(stack: $ReadOnlyArray<T>): T {
return stack[stack.length - 1];
}
export default class CommentsParser extends BaseParser {
addComment(comment: Comment): void {
if (this.filename) comment.loc.filename = this.filename;
this.state.trailingComments.push(comment);
this.state.leadingComments.push(comment);
}
processComment(node: Node): void {
if (node.type === "Program" && node.body.length > 0) return;
const stack = this.state.commentStack;
let firstChild, lastChild, trailingComments, i, j;
if (this.state.trailingComments.length > 0) {
// If the first comment in trailingComments comes after the
// current node, then we're good - all comments in the array will
// come after the node and so it's safe to add them as official
// trailingComments.
if (this.state.trailingComments[0].start >= node.end) {
trailingComments = this.state.trailingComments;
this.state.trailingComments = [];
} else {
// Otherwise, if the first comment doesn't come after the
// current node, that means we have a mix of leading and trailing
// comments in the array and that leadingComments contains the
// same items as trailingComments. Reset trailingComments to
// zero items and we'll handle this by evaluating leadingComments
// later.
this.state.trailingComments.length = 0;
}
} else {
if (stack.length > 0) {
const lastInStack = last(stack);
if (
lastInStack.trailingComments &&
lastInStack.trailingComments[0].start >= node.end
) {
trailingComments = lastInStack.trailingComments;
lastInStack.trailingComments = null;
}
}
}
// Eating the stack.
if (stack.length > 0 && last(stack).start >= node.start) {
firstChild = stack.pop();
}
while (stack.length > 0 && last(stack).start >= node.start) {
lastChild = stack.pop();
}
if (!lastChild && firstChild) lastChild = firstChild;
// Attach comments that follow a trailing comma on the last
// property in an object literal or a trailing comma in function arguments
// as trailing comments
if (firstChild && this.state.leadingComments.length > 0) {
const lastComment = last(this.state.leadingComments);
if (firstChild.type === "ObjectProperty") {
if (lastComment.start >= node.start) {
if (this.state.commentPreviousNode) {
for (j = 0; j < this.state.leadingComments.length; j++) {
if (
this.state.leadingComments[j].end <
this.state.commentPreviousNode.end
) {
this.state.leadingComments.splice(j, 1);
j--;
}
}
if (this.state.leadingComments.length > 0) {
firstChild.trailingComments = this.state.leadingComments;
this.state.leadingComments = [];
}
}
}
} else if (
node.type === "CallExpression" &&
node.arguments &&
node.arguments.length
) {
const lastArg = last(node.arguments);
if (
lastArg &&
lastComment.start >= lastArg.start &&
lastComment.end <= node.end
) {
if (this.state.commentPreviousNode) {
if (this.state.leadingComments.length > 0) {
lastArg.trailingComments = this.state.leadingComments;
this.state.leadingComments = [];
}
}
}
}
}
if (lastChild) {
if (lastChild.leadingComments) {
if (
lastChild !== node &&
lastChild.leadingComments.length > 0 &&
last(lastChild.leadingComments).end <= node.start
) {
node.leadingComments = lastChild.leadingComments;
lastChild.leadingComments = null;
} else {
// A leading comment for an anonymous class had been stolen by its first ClassMethod,
// so this takes back the leading comment.
// See also: https://github.com/eslint/espree/issues/158
for (i = lastChild.leadingComments.length - 2; i >= 0; --i) {
if (lastChild.leadingComments[i].end <= node.start) {
node.leadingComments = lastChild.leadingComments.splice(0, i + 1);
break;
}
}
}
}
} else if (this.state.leadingComments.length > 0) {
if (last(this.state.leadingComments).end <= node.start) {
if (this.state.commentPreviousNode) {
for (j = 0; j < this.state.leadingComments.length; j++) {
if (
this.state.leadingComments[j].end <
this.state.commentPreviousNode.end
) {
this.state.leadingComments.splice(j, 1);
j--;
}
}
}
if (this.state.leadingComments.length > 0) {
node.leadingComments = this.state.leadingComments;
this.state.leadingComments = [];
}
} else {
// https://github.com/eslint/espree/issues/2
//
// In special cases, such as return (without a value) and
// debugger, all comments will end up as leadingComments and
// will otherwise be eliminated. This step runs when the
// commentStack is empty and there are comments left
// in leadingComments.
//
// This loop figures out the stopping point between the actual
// leading and trailing comments by finding the location of the
// first comment that comes after the given node.
for (i = 0; i < this.state.leadingComments.length; i++) {
if (this.state.leadingComments[i].end > node.start) {
break;
}
}
// Split the array based on the location of the first comment
// that comes after the node. Keep in mind that this could
// result in an empty array, and if so, the array must be
// deleted.
const leadingComments = this.state.leadingComments.slice(0, i);
node.leadingComments =
leadingComments.length === 0 ? null : leadingComments;
// Similarly, trailing comments are attached later. The variable
// must be reset to null if there are no trailing comments.
trailingComments = this.state.leadingComments.slice(i);
if (trailingComments.length === 0) {
trailingComments = null;
}
}
}
this.state.commentPreviousNode = node;
if (trailingComments) {
if (
trailingComments.length &&
trailingComments[0].start >= node.start &&
last(trailingComments).end <= node.end
) {
node.innerComments = trailingComments;
} else {
node.trailingComments = trailingComments;
}
}
stack.push(node);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
// @flow
import type { Options } from "../options";
import type { File } from "../types";
import { getOptions } from "../options";
import StatementParser from "./statement";
export const plugins: {
[name: string]: (superClass: Class<Parser>) => Class<Parser>,
} = {};
export default class Parser extends StatementParser {
constructor(options: ?Options, input: string) {
options = getOptions(options);
super(options, input);
this.options = options;
this.inModule = this.options.sourceType === "module";
this.input = input;
this.plugins = pluginsMap(this.options.plugins);
this.filename = options.sourceFilename;
// If enabled, skip leading hashbang line.
if (
this.state.pos === 0 &&
this.input[0] === "#" &&
this.input[1] === "!"
) {
this.skipLineComment(2);
}
}
parse(): File {
const file = this.startNode();
const program = this.startNode();
this.nextToken();
return this.parseTopLevel(file, program);
}
}
function pluginsMap(
pluginList: $ReadOnlyArray<string>,
): { [key: string]: boolean } {
const pluginMap = {};
for (const name of pluginList) {
pluginMap[name] = true;
}
return pluginMap;
}

View File

@ -0,0 +1,31 @@
// @flow
import { getLineInfo, type Position } from "../util/location";
import CommentsParser from "./comments";
// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.
export default class LocationParser extends CommentsParser {
raise(
pos: number,
message: string,
missingPluginNames?: Array<string>,
): empty {
const loc = getLineInfo(this.input, pos);
message += ` (${loc.line}:${loc.column})`;
// $FlowIgnore
const err: SyntaxError & { pos: number, loc: Position } = new SyntaxError(
message,
);
err.pos = pos;
err.loc = loc;
if (missingPluginNames) {
err.missingPlugin = missingPluginNames;
}
throw err;
}
}

View File

@ -0,0 +1,403 @@
// @flow
import { types as tt, type TokenType } from "../tokenizer/types";
import type {
TSParameterProperty,
Decorator,
Expression,
Identifier,
Node,
ObjectExpression,
ObjectPattern,
Pattern,
RestElement,
SpreadElement,
} from "../types";
import type { Pos, Position } from "../util/location";
import { NodeUtils } from "./node";
export default class LValParser extends NodeUtils {
// Forward-declaration: defined in expression.js
+checkReservedWord: (
word: string,
startLoc: number,
checkKeywords: boolean,
isBinding: boolean,
) => void;
+parseIdentifier: (liberal?: boolean) => Identifier;
+parseMaybeAssign: (
noIn?: ?boolean,
refShorthandDefaultPos?: ?Pos,
afterLeftParse?: Function,
refNeedsArrowPos?: ?Pos,
) => Expression;
+parseObj: <T: ObjectPattern | ObjectExpression>(
isPattern: boolean,
refShorthandDefaultPos?: ?Pos,
) => T;
// Forward-declaration: defined in statement.js
+parseDecorator: () => Decorator;
// Convert existing expression atom to assignable pattern
// if possible.
toAssignable(
node: Node,
isBinding: ?boolean,
contextDescription: string,
): Node {
if (node) {
switch (node.type) {
case "Identifier":
case "ObjectPattern":
case "ArrayPattern":
case "AssignmentPattern":
break;
case "ObjectExpression":
node.type = "ObjectPattern";
for (const [index, prop] of node.properties.entries()) {
this.toAssignableObjectExpressionProp(
prop,
isBinding,
index === node.properties.length - 1,
);
}
break;
case "ObjectProperty":
this.toAssignable(node.value, isBinding, contextDescription);
break;
case "SpreadElement": {
this.checkToRestConversion(node);
node.type = "RestElement";
const arg = node.argument;
this.toAssignable(arg, isBinding, contextDescription);
break;
}
case "ArrayExpression":
node.type = "ArrayPattern";
this.toAssignableList(node.elements, isBinding, contextDescription);
break;
case "AssignmentExpression":
if (node.operator === "=") {
node.type = "AssignmentPattern";
delete node.operator;
} else {
this.raise(
node.left.end,
"Only '=' operator can be used for specifying default value.",
);
}
break;
case "MemberExpression":
if (!isBinding) break;
default: {
const message =
"Invalid left-hand side" +
(contextDescription
? " in " + contextDescription
: /* istanbul ignore next */ "expression");
this.raise(node.start, message);
}
}
}
return node;
}
toAssignableObjectExpressionProp(
prop: Node,
isBinding: ?boolean,
isLast: boolean,
) {
if (prop.type === "ObjectMethod") {
const error =
prop.kind === "get" || prop.kind === "set"
? "Object pattern can't contain getter or setter"
: "Object pattern can't contain methods";
this.raise(prop.key.start, error);
} else if (prop.type === "SpreadElement" && !isLast) {
this.raise(
prop.start,
"The rest element has to be the last element when destructuring",
);
} else {
this.toAssignable(prop, isBinding, "object destructuring pattern");
}
}
// Convert list of expression atoms to binding list.
toAssignableList(
exprList: Expression[],
isBinding: ?boolean,
contextDescription: string,
): $ReadOnlyArray<Pattern> {
let end = exprList.length;
if (end) {
const last = exprList[end - 1];
if (last && last.type === "RestElement") {
--end;
} else if (last && last.type === "SpreadElement") {
last.type = "RestElement";
const arg = last.argument;
this.toAssignable(arg, isBinding, contextDescription);
if (
arg.type !== "Identifier" &&
arg.type !== "MemberExpression" &&
arg.type !== "ArrayPattern"
) {
this.unexpected(arg.start);
}
--end;
}
}
for (let i = 0; i < end; i++) {
const elt = exprList[i];
if (elt && elt.type === "SpreadElement")
this.raise(
elt.start,
"The rest element has to be the last element when destructuring",
);
if (elt) this.toAssignable(elt, isBinding, contextDescription);
}
return exprList;
}
// Convert list of expression atoms to a list of
toReferencedList(
exprList: $ReadOnlyArray<?Expression>,
): $ReadOnlyArray<?Expression> {
return exprList;
}
// Parses spread element.
parseSpread<T: RestElement | SpreadElement>(refShorthandDefaultPos: ?Pos): T {
const node = this.startNode();
this.next();
node.argument = this.parseMaybeAssign(false, refShorthandDefaultPos);
return this.finishNode(node, "SpreadElement");
}
parseRest(): RestElement {
const node = this.startNode();
this.next();
node.argument = this.parseBindingAtom();
return this.finishNode(node, "RestElement");
}
shouldAllowYieldIdentifier(): boolean {
return (
this.match(tt._yield) && !this.state.strict && !this.state.inGenerator
);
}
parseBindingIdentifier(): Identifier {
return this.parseIdentifier(this.shouldAllowYieldIdentifier());
}
// Parses lvalue (assignable) atom.
parseBindingAtom(): Pattern {
switch (this.state.type) {
case tt._yield:
case tt.name:
return this.parseBindingIdentifier();
case tt.bracketL: {
const node = this.startNode();
this.next();
node.elements = this.parseBindingList(tt.bracketR, true);
return this.finishNode(node, "ArrayPattern");
}
case tt.braceL:
return this.parseObj(true);
default:
throw this.unexpected();
}
}
parseBindingList(
close: TokenType,
allowEmpty?: boolean,
allowModifiers?: boolean,
): $ReadOnlyArray<Pattern | TSParameterProperty> {
const elts: Array<Pattern | TSParameterProperty> = [];
let first = true;
while (!this.eat(close)) {
if (first) {
first = false;
} else {
this.expect(tt.comma);
}
if (allowEmpty && this.match(tt.comma)) {
// $FlowFixMe This method returns `$ReadOnlyArray<?Pattern>` if `allowEmpty` is set.
elts.push(null);
} else if (this.eat(close)) {
break;
} else if (this.match(tt.ellipsis)) {
elts.push(this.parseAssignableListItemTypes(this.parseRest()));
this.expect(close);
break;
} else {
const decorators = [];
if (this.match(tt.at) && this.hasPlugin("decorators2")) {
this.raise(
this.state.start,
"Stage 2 decorators cannot be used to decorate parameters",
);
}
while (this.match(tt.at)) {
decorators.push(this.parseDecorator());
}
elts.push(this.parseAssignableListItem(allowModifiers, decorators));
}
}
return elts;
}
parseAssignableListItem(
allowModifiers: ?boolean,
decorators: Decorator[],
): Pattern | TSParameterProperty {
const left = this.parseMaybeDefault();
this.parseAssignableListItemTypes(left);
const elt = this.parseMaybeDefault(left.start, left.loc.start, left);
if (decorators.length) {
left.decorators = decorators;
}
return elt;
}
parseAssignableListItemTypes(param: Pattern): Pattern {
return param;
}
// Parses assignment pattern around given atom if possible.
parseMaybeDefault(
startPos?: ?number,
startLoc?: ?Position,
left?: ?Pattern,
): Pattern {
startLoc = startLoc || this.state.startLoc;
startPos = startPos || this.state.start;
left = left || this.parseBindingAtom();
if (!this.eat(tt.eq)) return left;
const node = this.startNodeAt(startPos, startLoc);
node.left = left;
node.right = this.parseMaybeAssign();
return this.finishNode(node, "AssignmentPattern");
}
// Verify that a node is an lval — something that can be assigned
// to.
checkLVal(
expr: Expression,
isBinding: ?boolean,
checkClashes: ?{ [key: string]: boolean },
contextDescription: string,
): void {
switch (expr.type) {
case "Identifier":
this.checkReservedWord(expr.name, expr.start, false, true);
if (checkClashes) {
// we need to prefix this with an underscore for the cases where we have a key of
// `__proto__`. there's a bug in old V8 where the following wouldn't work:
//
// > var obj = Object.create(null);
// undefined
// > obj.__proto__
// null
// > obj.__proto__ = true;
// true
// > obj.__proto__
// null
const key = `_${expr.name}`;
if (checkClashes[key]) {
this.raise(expr.start, "Argument name clash in strict mode");
} else {
checkClashes[key] = true;
}
}
break;
case "MemberExpression":
if (isBinding) this.raise(expr.start, "Binding member expression");
break;
case "ObjectPattern":
for (let prop of expr.properties) {
if (prop.type === "ObjectProperty") prop = prop.value;
this.checkLVal(
prop,
isBinding,
checkClashes,
"object destructuring pattern",
);
}
break;
case "ArrayPattern":
for (const elem of expr.elements) {
if (elem)
this.checkLVal(
elem,
isBinding,
checkClashes,
"array destructuring pattern",
);
}
break;
case "AssignmentPattern":
this.checkLVal(
expr.left,
isBinding,
checkClashes,
"assignment pattern",
);
break;
case "RestElement":
this.checkLVal(expr.argument, isBinding, checkClashes, "rest element");
break;
default: {
const message =
(isBinding
? /* istanbul ignore next */ "Binding invalid"
: "Invalid") +
" left-hand side" +
(contextDescription
? " in " + contextDescription
: /* istanbul ignore next */ "expression");
this.raise(expr.start, message);
}
}
}
checkToRestConversion(node: SpreadElement): void {
const validArgumentTypes = ["Identifier", "MemberExpression"];
if (validArgumentTypes.indexOf(node.argument.type) !== -1) {
return;
}
this.raise(node.argument.start, "Invalid rest operator's argument");
}
}

View File

@ -0,0 +1,98 @@
// @flow
import Parser from "./index";
import UtilParser from "./util";
import { SourceLocation, type Position } from "../util/location";
import type { Comment, Node as NodeType, NodeBase } from "../types";
// Start an AST node, attaching a start offset.
const commentKeys = ["leadingComments", "trailingComments", "innerComments"];
class Node implements NodeBase {
constructor(parser: Parser, pos: number, loc: Position) {
this.type = "";
this.start = pos;
this.end = 0;
this.loc = new SourceLocation(loc);
if (parser && parser.options.ranges) this.range = [pos, 0];
if (parser && parser.filename) this.loc.filename = parser.filename;
}
type: string;
start: number;
end: number;
loc: SourceLocation;
range: [number, number];
leadingComments: ?Array<Comment>;
trailingComments: ?Array<Comment>;
innerComments: ?Array<Comment>;
extra: { [key: string]: any };
__clone(): this {
// $FlowIgnore
const node2: any = new Node();
for (const key in this) {
// Do not clone comments that are already attached to the node
if (commentKeys.indexOf(key) < 0) {
// $FlowIgnore
node2[key] = this[key];
}
}
return node2;
}
}
export class NodeUtils extends UtilParser {
startNode<T: NodeType>(): T {
// $FlowIgnore
return new Node(this, this.state.start, this.state.startLoc);
}
startNodeAt<T: NodeType>(pos: number, loc: Position): T {
// $FlowIgnore
return new Node(this, pos, loc);
}
/** Start a new node with a previous node's location. */
startNodeAtNode<T: NodeType>(type: NodeType): T {
return this.startNodeAt(type.start, type.loc.start);
}
// Finish an AST node, adding `type` and `end` properties.
finishNode<T: NodeType>(node: T, type: string): T {
return this.finishNodeAt(
node,
type,
this.state.lastTokEnd,
this.state.lastTokEndLoc,
);
}
// Finish node at given position
finishNodeAt<T: NodeType>(
node: T,
type: string,
pos: number,
loc: Position,
): T {
node.type = type;
node.end = pos;
node.loc.end = loc;
if (this.options.ranges) node.range[1] = pos;
this.processComment(node);
return node;
}
/**
* Reset the start location of node to the start location of locationNode
*/
resetStartLocationFromNode(node: NodeBase, locationNode: NodeBase): void {
node.start = locationNode.start;
node.loc.start = locationNode.loc.start;
if (this.options.ranges) node.range[0] = locationNode.range[0];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
// @flow
import { types as tt, type TokenType } from "../tokenizer/types";
import Tokenizer from "../tokenizer";
import type { Node } from "../types";
import { lineBreak } from "../util/whitespace";
// ## Parser utilities
export default class UtilParser extends Tokenizer {
// TODO
addExtra(node: Node, key: string, val: any): void {
if (!node) return;
const extra = (node.extra = node.extra || {});
extra[key] = val;
}
// TODO
isRelational(op: "<" | ">"): boolean {
return this.match(tt.relational) && this.state.value === op;
}
// TODO
expectRelational(op: "<" | ">"): void {
if (this.isRelational(op)) {
this.next();
} else {
this.unexpected(null, tt.relational);
}
}
// eat() for relational operators.
eatRelational(op: "<" | ">"): boolean {
if (this.isRelational(op)) {
this.next();
return true;
}
return false;
}
// Tests whether parsed token is a contextual keyword.
isContextual(name: string): boolean {
return this.match(tt.name) && this.state.value === name;
}
isLookaheadContextual(name: string): boolean {
const l = this.lookahead();
return l.type === tt.name && l.value === name;
}
// Consumes contextual keyword if possible.
eatContextual(name: string): boolean {
return this.state.value === name && this.eat(tt.name);
}
// Asserts that following token is given contextual keyword.
expectContextual(name: string, message?: string): void {
if (!this.eatContextual(name)) this.unexpected(null, message);
}
// Test whether a semicolon can be inserted at the current position.
canInsertSemicolon(): boolean {
return (
this.match(tt.eof) ||
this.match(tt.braceR) ||
this.hasPrecedingLineBreak()
);
}
hasPrecedingLineBreak(): boolean {
return lineBreak.test(
this.input.slice(this.state.lastTokEnd, this.state.start),
);
}
// TODO
isLineTerminator(): boolean {
return this.eat(tt.semi) || this.canInsertSemicolon();
}
// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.
semicolon(): void {
if (!this.isLineTerminator()) this.unexpected(null, tt.semi);
}
// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error at given pos.
expect(type: TokenType, pos?: ?number): void {
this.eat(type) || this.unexpected(pos, type);
}
// Raise an unexpected token error. Can take the expected token type
// instead of a message string.
unexpected(
pos: ?number,
messageOrType: string | TokenType = "Unexpected token",
): empty {
if (typeof messageOrType !== "string") {
messageOrType = `Unexpected token, expected ${messageOrType.label}`;
}
throw this.raise(pos != null ? pos : this.state.start, messageOrType);
}
expectPlugin(name: string, pos?: ?number): true {
if (!this.hasPlugin(name)) {
throw this.raise(
pos != null ? pos : this.state.start,
`This experimental syntax requires enabling the parser plugin: '${name}'`,
[name],
);
}
return true;
}
expectOnePlugin(names: Array<string>, pos?: ?number): void {
if (!names.some(n => this.hasPlugin(n))) {
throw this.raise(
pos != null ? pos : this.state.start,
`This experimental syntax requires enabling one of the following parser plugin(s): '${names.join(
", ",
)}'`,
names,
);
}
}
}

View File

@ -0,0 +1,342 @@
// @flow
import { types as tt, TokenType } from "../tokenizer/types";
import type Parser from "../parser";
import * as N from "../types";
import type { Pos, Position } from "../util/location";
function isSimpleProperty(node: N.Node): boolean {
return (
node != null &&
node.type === "Property" &&
node.kind === "init" &&
node.method === false
);
}
export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass {
estreeParseRegExpLiteral({ pattern, flags }: N.RegExpLiteral): N.Node {
let regex = null;
try {
regex = new RegExp(pattern, flags);
} catch (e) {
// In environments that don't support these flags value will
// be null as the regex can't be represented natively.
}
const node = this.estreeParseLiteral(regex);
node.regex = { pattern, flags };
return node;
}
estreeParseLiteral(value: any): N.Node {
return this.parseLiteral(value, "Literal");
}
directiveToStmt(directive: N.Directive): N.ExpressionStatement {
const directiveLiteral = directive.value;
const stmt = this.startNodeAt(directive.start, directive.loc.start);
const expression = this.startNodeAt(
directiveLiteral.start,
directiveLiteral.loc.start,
);
expression.value = directiveLiteral.value;
expression.raw = directiveLiteral.extra.raw;
stmt.expression = this.finishNodeAt(
expression,
"Literal",
directiveLiteral.end,
directiveLiteral.loc.end,
);
stmt.directive = directiveLiteral.extra.raw.slice(1, -1);
return this.finishNodeAt(
stmt,
"ExpressionStatement",
directive.end,
directive.loc.end,
);
}
// ==================================
// Overrides
// ==================================
checkDeclaration(node: N.Pattern): void {
if (isSimpleProperty(node)) {
// $FlowFixMe
this.checkDeclaration(node.value);
} else {
super.checkDeclaration(node);
}
}
checkGetterSetterParamCount(prop: N.ObjectMethod | N.ClassMethod): void {
const paramCount = prop.kind === "get" ? 0 : 1;
// $FlowFixMe (prop.value present for ObjectMethod, but for ClassMethod should use prop.params?)
if (prop.value.params.length !== paramCount) {
const start = prop.start;
if (prop.kind === "get") {
this.raise(start, "getter should have no params");
} else {
this.raise(start, "setter should have exactly one param");
}
}
}
checkLVal(
expr: N.Expression,
isBinding: ?boolean,
checkClashes: ?{ [key: string]: boolean },
contextDescription: string,
): void {
switch (expr.type) {
case "ObjectPattern":
expr.properties.forEach(prop => {
this.checkLVal(
prop.type === "Property" ? prop.value : prop,
isBinding,
checkClashes,
"object destructuring pattern",
);
});
break;
default:
super.checkLVal(expr, isBinding, checkClashes, contextDescription);
}
}
checkPropClash(
prop: N.ObjectMember,
propHash: { [key: string]: boolean },
): void {
if (prop.computed || !isSimpleProperty(prop)) return;
const key = prop.key;
// It is either an Identifier or a String/NumericLiteral
const name = key.type === "Identifier" ? key.name : String(key.value);
if (name === "__proto__") {
if (propHash.proto)
this.raise(key.start, "Redefinition of __proto__ property");
propHash.proto = true;
}
}
isStrictBody(
node: { body: N.BlockStatement },
isExpression: ?boolean,
): boolean {
if (!isExpression && node.body.body.length > 0) {
for (const directive of node.body.body) {
if (
directive.type === "ExpressionStatement" &&
directive.expression.type === "Literal"
) {
if (directive.expression.value === "use strict") return true;
} else {
// Break for the first non literal expression
break;
}
}
}
return false;
}
isValidDirective(stmt: N.Statement): boolean {
return (
stmt.type === "ExpressionStatement" &&
stmt.expression.type === "Literal" &&
typeof stmt.expression.value === "string" &&
(!stmt.expression.extra || !stmt.expression.extra.parenthesized)
);
}
stmtToDirective(stmt: N.Statement): N.Directive {
const directive = super.stmtToDirective(stmt);
const value = stmt.expression.value;
// Reset value to the actual value as in estree mode we want
// the stmt to have the real value and not the raw value
directive.value.value = value;
return directive;
}
parseBlockBody(
node: N.BlockStatementLike,
allowDirectives: ?boolean,
topLevel: boolean,
end: TokenType,
): void {
super.parseBlockBody(node, allowDirectives, topLevel, end);
const directiveStatements = node.directives.map(d =>
this.directiveToStmt(d),
);
node.body = directiveStatements.concat(node.body);
delete node.directives;
}
pushClassMethod(
classBody: N.ClassBody,
method: N.ClassMethod,
isGenerator: boolean,
isAsync: boolean,
isConstructor: boolean,
): void {
this.parseMethod(
method,
isGenerator,
isAsync,
isConstructor,
"MethodDefinition",
);
if (method.typeParameters) {
// $FlowIgnore
method.value.typeParameters = method.typeParameters;
delete method.typeParameters;
}
classBody.body.push(method);
}
parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression {
switch (this.state.type) {
case tt.regexp:
return this.estreeParseRegExpLiteral(this.state.value);
case tt.num:
case tt.string:
return this.estreeParseLiteral(this.state.value);
case tt._null:
return this.estreeParseLiteral(null);
case tt._true:
return this.estreeParseLiteral(true);
case tt._false:
return this.estreeParseLiteral(false);
default:
return super.parseExprAtom(refShorthandDefaultPos);
}
}
parseLiteral<T: N.Literal>(
value: any,
type: /*T["kind"]*/ string,
startPos?: number,
startLoc?: Position,
): T {
const node = super.parseLiteral(value, type, startPos, startLoc);
node.raw = node.extra.raw;
delete node.extra;
return node;
}
parseMethod<T: N.MethodLike>(
node: T,
isGenerator: boolean,
isAsync: boolean,
isConstructor: boolean,
type: string,
): T {
let funcNode = this.startNode();
funcNode.kind = node.kind; // provide kind, so super method correctly sets state
funcNode = super.parseMethod(
funcNode,
isGenerator,
isAsync,
isConstructor,
"FunctionExpression",
);
delete funcNode.kind;
// $FlowIgnore
node.value = funcNode;
return this.finishNode(node, type);
}
parseObjectMethod(
prop: N.ObjectMethod,
isGenerator: boolean,
isAsync: boolean,
isPattern: boolean,
): ?N.ObjectMethod {
const node: N.EstreeProperty = (super.parseObjectMethod(
prop,
isGenerator,
isAsync,
isPattern,
): any);
if (node) {
node.type = "Property";
if (node.kind === "method") node.kind = "init";
node.shorthand = false;
}
return (node: any);
}
parseObjectProperty(
prop: N.ObjectProperty,
startPos: ?number,
startLoc: ?Position,
isPattern: boolean,
refShorthandDefaultPos: ?Pos,
): ?N.ObjectProperty {
const node: N.EstreeProperty = (super.parseObjectProperty(
prop,
startPos,
startLoc,
isPattern,
refShorthandDefaultPos,
): any);
if (node) {
node.kind = "init";
node.type = "Property";
}
return (node: any);
}
toAssignable(
node: N.Node,
isBinding: ?boolean,
contextDescription: string,
): N.Node {
if (isSimpleProperty(node)) {
this.toAssignable(node.value, isBinding, contextDescription);
return node;
}
return super.toAssignable(node, isBinding, contextDescription);
}
toAssignableObjectExpressionProp(
prop: N.Node,
isBinding: ?boolean,
isLast: boolean,
) {
if (prop.kind === "get" || prop.kind === "set") {
this.raise(
prop.key.start,
"Object pattern can't contain getter or setter",
);
} else if (prop.method) {
this.raise(prop.key.start, "Object pattern can't contain methods");
} else {
super.toAssignableObjectExpressionProp(prop, isBinding, isLast);
}
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,551 @@
// @flow
import XHTMLEntities from "./xhtml";
import type Parser from "../../parser";
import { TokenType, types as tt } from "../../tokenizer/types";
import { TokContext, types as tc } from "../../tokenizer/context";
import * as N from "../../types";
import { isIdentifierChar, isIdentifierStart } from "../../util/identifier";
import type { Pos, Position } from "../../util/location";
import { isNewLine } from "../../util/whitespace";
const HEX_NUMBER = /^[\da-fA-F]+$/;
const DECIMAL_NUMBER = /^\d+$/;
tc.j_oTag = new TokContext("<tag", false);
tc.j_cTag = new TokContext("</tag", false);
tc.j_expr = new TokContext("<tag>...</tag>", true, true);
tt.jsxName = new TokenType("jsxName");
tt.jsxText = new TokenType("jsxText", { beforeExpr: true });
tt.jsxTagStart = new TokenType("jsxTagStart", { startsExpr: true });
tt.jsxTagEnd = new TokenType("jsxTagEnd");
tt.jsxTagStart.updateContext = function() {
this.state.context.push(tc.j_expr); // treat as beginning of JSX expression
this.state.context.push(tc.j_oTag); // start opening tag context
this.state.exprAllowed = false;
};
tt.jsxTagEnd.updateContext = function(prevType) {
const out = this.state.context.pop();
if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) {
this.state.context.pop();
this.state.exprAllowed = this.curContext() === tc.j_expr;
} else {
this.state.exprAllowed = true;
}
};
function isFragment(object: ?N.JSXElement): boolean {
return object
? object.type === "JSXOpeningFragment" ||
object.type === "JSXClosingFragment"
: false;
}
// Transforms JSX element name to string.
function getQualifiedJSXName(
object: N.JSXIdentifier | N.JSXNamespacedName | N.JSXMemberExpression,
): string {
if (object.type === "JSXIdentifier") {
return object.name;
}
if (object.type === "JSXNamespacedName") {
return object.namespace.name + ":" + object.name.name;
}
if (object.type === "JSXMemberExpression") {
return (
getQualifiedJSXName(object.object) +
"." +
getQualifiedJSXName(object.property)
);
}
// istanbul ignore next
throw new Error("Node had unexpected type: " + object.type);
}
export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass {
// Reads inline JSX contents token.
jsxReadToken(): void {
let out = "";
let chunkStart = this.state.pos;
for (;;) {
if (this.state.pos >= this.input.length) {
this.raise(this.state.start, "Unterminated JSX contents");
}
const ch = this.input.charCodeAt(this.state.pos);
switch (ch) {
case 60: // "<"
case 123: // "{"
if (this.state.pos === this.state.start) {
if (ch === 60 && this.state.exprAllowed) {
++this.state.pos;
return this.finishToken(tt.jsxTagStart);
}
return this.getTokenFromCode(ch);
}
out += this.input.slice(chunkStart, this.state.pos);
return this.finishToken(tt.jsxText, out);
case 38: // "&"
out += this.input.slice(chunkStart, this.state.pos);
out += this.jsxReadEntity();
chunkStart = this.state.pos;
break;
default:
if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.state.pos);
out += this.jsxReadNewLine(true);
chunkStart = this.state.pos;
} else {
++this.state.pos;
}
}
}
}
jsxReadNewLine(normalizeCRLF: boolean): string {
const ch = this.input.charCodeAt(this.state.pos);
let out;
++this.state.pos;
if (ch === 13 && this.input.charCodeAt(this.state.pos) === 10) {
++this.state.pos;
out = normalizeCRLF ? "\n" : "\r\n";
} else {
out = String.fromCharCode(ch);
}
++this.state.curLine;
this.state.lineStart = this.state.pos;
return out;
}
jsxReadString(quote: number): void {
let out = "";
let chunkStart = ++this.state.pos;
for (;;) {
if (this.state.pos >= this.input.length) {
this.raise(this.state.start, "Unterminated string constant");
}
const ch = this.input.charCodeAt(this.state.pos);
if (ch === quote) break;
if (ch === 38) {
// "&"
out += this.input.slice(chunkStart, this.state.pos);
out += this.jsxReadEntity();
chunkStart = this.state.pos;
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.state.pos);
out += this.jsxReadNewLine(false);
chunkStart = this.state.pos;
} else {
++this.state.pos;
}
}
out += this.input.slice(chunkStart, this.state.pos++);
return this.finishToken(tt.string, out);
}
jsxReadEntity(): string {
let str = "";
let count = 0;
let entity;
let ch = this.input[this.state.pos];
const startPos = ++this.state.pos;
while (this.state.pos < this.input.length && count++ < 10) {
ch = this.input[this.state.pos++];
if (ch === ";") {
if (str[0] === "#") {
if (str[1] === "x") {
str = str.substr(2);
if (HEX_NUMBER.test(str))
entity = String.fromCodePoint(parseInt(str, 16));
} else {
str = str.substr(1);
if (DECIMAL_NUMBER.test(str))
entity = String.fromCodePoint(parseInt(str, 10));
}
} else {
entity = XHTMLEntities[str];
}
break;
}
str += ch;
}
if (!entity) {
this.state.pos = startPos;
return "&";
}
return entity;
}
// Read a JSX identifier (valid tag or attribute name).
//
// Optimized version since JSX identifiers can"t contain
// escape characters and so can be read as single slice.
// Also assumes that first character was already checked
// by isIdentifierStart in readToken.
jsxReadWord(): void {
let ch;
const start = this.state.pos;
do {
ch = this.input.charCodeAt(++this.state.pos);
} while (isIdentifierChar(ch) || ch === 45); // "-"
return this.finishToken(
tt.jsxName,
this.input.slice(start, this.state.pos),
);
}
// Parse next token as JSX identifier
jsxParseIdentifier(): N.JSXIdentifier {
const node = this.startNode();
if (this.match(tt.jsxName)) {
node.name = this.state.value;
} else if (this.state.type.keyword) {
node.name = this.state.type.keyword;
} else {
this.unexpected();
}
this.next();
return this.finishNode(node, "JSXIdentifier");
}
// Parse namespaced identifier.
jsxParseNamespacedName(): N.JSXNamespacedName {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
const name = this.jsxParseIdentifier();
if (!this.eat(tt.colon)) return name;
const node = this.startNodeAt(startPos, startLoc);
node.namespace = name;
node.name = this.jsxParseIdentifier();
return this.finishNode(node, "JSXNamespacedName");
}
// Parses element name in any form - namespaced, member
// or single identifier.
jsxParseElementName(): N.JSXNamespacedName | N.JSXMemberExpression {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
let node = this.jsxParseNamespacedName();
while (this.eat(tt.dot)) {
const newNode = this.startNodeAt(startPos, startLoc);
newNode.object = node;
newNode.property = this.jsxParseIdentifier();
node = this.finishNode(newNode, "JSXMemberExpression");
}
return node;
}
// Parses any type of JSX attribute value.
jsxParseAttributeValue(): N.Expression {
let node;
switch (this.state.type) {
case tt.braceL:
node = this.jsxParseExpressionContainer();
if (node.expression.type === "JSXEmptyExpression") {
throw this.raise(
node.start,
"JSX attributes must only be assigned a non-empty expression",
);
} else {
return node;
}
case tt.jsxTagStart:
case tt.string:
return this.parseExprAtom();
default:
throw this.raise(
this.state.start,
"JSX value should be either an expression or a quoted JSX text",
);
}
}
// JSXEmptyExpression is unique type since it doesn't actually parse anything,
// and so it should start at the end of last read token (left brace) and finish
// at the beginning of the next one (right brace).
jsxParseEmptyExpression(): N.JSXEmptyExpression {
const node = this.startNodeAt(
this.state.lastTokEnd,
this.state.lastTokEndLoc,
);
return this.finishNodeAt(
node,
"JSXEmptyExpression",
this.state.start,
this.state.startLoc,
);
}
// Parse JSX spread child
jsxParseSpreadChild(): N.JSXSpreadChild {
const node = this.startNode();
this.expect(tt.braceL);
this.expect(tt.ellipsis);
node.expression = this.parseExpression();
this.expect(tt.braceR);
return this.finishNode(node, "JSXSpreadChild");
}
// Parses JSX expression enclosed into curly brackets.
jsxParseExpressionContainer(): N.JSXExpressionContainer {
const node = this.startNode();
this.next();
if (this.match(tt.braceR)) {
node.expression = this.jsxParseEmptyExpression();
} else {
node.expression = this.parseExpression();
}
this.expect(tt.braceR);
return this.finishNode(node, "JSXExpressionContainer");
}
// Parses following JSX attribute name-value pair.
jsxParseAttribute(): N.JSXAttribute {
const node = this.startNode();
if (this.eat(tt.braceL)) {
this.expect(tt.ellipsis);
node.argument = this.parseMaybeAssign();
this.expect(tt.braceR);
return this.finishNode(node, "JSXSpreadAttribute");
}
node.name = this.jsxParseNamespacedName();
node.value = this.eat(tt.eq) ? this.jsxParseAttributeValue() : null;
return this.finishNode(node, "JSXAttribute");
}
// Parses JSX opening tag starting after "<".
jsxParseOpeningElementAt(
startPos: number,
startLoc: Position,
): N.JSXOpeningElement {
const node = this.startNodeAt(startPos, startLoc);
if (this.match(tt.jsxTagEnd)) {
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningFragment");
}
node.attributes = [];
node.name = this.jsxParseElementName();
while (!this.match(tt.slash) && !this.match(tt.jsxTagEnd)) {
node.attributes.push(this.jsxParseAttribute());
}
node.selfClosing = this.eat(tt.slash);
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningElement");
}
// Parses JSX closing tag starting after "</".
jsxParseClosingElementAt(
startPos: number,
startLoc: Position,
): N.JSXClosingElement {
const node = this.startNodeAt(startPos, startLoc);
if (this.match(tt.jsxTagEnd)) {
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXClosingFragment");
}
node.name = this.jsxParseElementName();
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXClosingElement");
}
// Parses entire JSX element, including it"s opening tag
// (starting after "<"), attributes, contents and closing tag.
jsxParseElementAt(startPos: number, startLoc: Position): N.JSXElement {
const node = this.startNodeAt(startPos, startLoc);
const children = [];
const openingElement = this.jsxParseOpeningElementAt(startPos, startLoc);
let closingElement = null;
if (!openingElement.selfClosing) {
contents: for (;;) {
switch (this.state.type) {
case tt.jsxTagStart:
startPos = this.state.start;
startLoc = this.state.startLoc;
this.next();
if (this.eat(tt.slash)) {
closingElement = this.jsxParseClosingElementAt(
startPos,
startLoc,
);
break contents;
}
children.push(this.jsxParseElementAt(startPos, startLoc));
break;
case tt.jsxText:
children.push(this.parseExprAtom());
break;
case tt.braceL:
if (this.lookahead().type === tt.ellipsis) {
children.push(this.jsxParseSpreadChild());
} else {
children.push(this.jsxParseExpressionContainer());
}
break;
// istanbul ignore next - should never happen
default:
throw this.unexpected();
}
}
if (isFragment(openingElement) && !isFragment(closingElement)) {
this.raise(
// $FlowIgnore
closingElement.start,
"Expected corresponding JSX closing tag for <>",
);
} else if (!isFragment(openingElement) && isFragment(closingElement)) {
this.raise(
// $FlowIgnore
closingElement.start,
"Expected corresponding JSX closing tag for <" +
getQualifiedJSXName(openingElement.name) +
">",
);
} else if (!isFragment(openingElement) && !isFragment(closingElement)) {
if (
// $FlowIgnore
getQualifiedJSXName(closingElement.name) !==
getQualifiedJSXName(openingElement.name)
) {
this.raise(
// $FlowIgnore
closingElement.start,
"Expected corresponding JSX closing tag for <" +
getQualifiedJSXName(openingElement.name) +
">",
);
}
}
}
if (isFragment(openingElement)) {
node.openingFragment = openingElement;
node.closingFragment = closingElement;
} else {
node.openingElement = openingElement;
node.closingElement = closingElement;
}
node.children = children;
if (this.match(tt.relational) && this.state.value === "<") {
this.raise(
this.state.start,
"Adjacent JSX elements must be wrapped in an enclosing tag",
);
}
return isFragment(openingElement)
? this.finishNode(node, "JSXFragment")
: this.finishNode(node, "JSXElement");
}
// Parses entire JSX element from current position.
jsxParseElement(): N.JSXElement {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
this.next();
return this.jsxParseElementAt(startPos, startLoc);
}
// ==================================
// Overrides
// ==================================
parseExprAtom(refShortHandDefaultPos: ?Pos): N.Expression {
if (this.match(tt.jsxText)) {
return this.parseLiteral(this.state.value, "JSXText");
} else if (this.match(tt.jsxTagStart)) {
return this.jsxParseElement();
} else {
return super.parseExprAtom(refShortHandDefaultPos);
}
}
readToken(code: number): void {
if (this.state.inPropertyName) return super.readToken(code);
const context = this.curContext();
if (context === tc.j_expr) {
return this.jsxReadToken();
}
if (context === tc.j_oTag || context === tc.j_cTag) {
if (isIdentifierStart(code)) {
return this.jsxReadWord();
}
if (code === 62) {
++this.state.pos;
return this.finishToken(tt.jsxTagEnd);
}
if ((code === 34 || code === 39) && context === tc.j_oTag) {
return this.jsxReadString(code);
}
}
if (code === 60 && this.state.exprAllowed) {
++this.state.pos;
return this.finishToken(tt.jsxTagStart);
}
return super.readToken(code);
}
updateContext(prevType: TokenType): void {
if (this.match(tt.braceL)) {
const curContext = this.curContext();
if (curContext === tc.j_oTag) {
this.state.context.push(tc.braceExpression);
} else if (curContext === tc.j_expr) {
this.state.context.push(tc.templateQuasi);
} else {
super.updateContext(prevType);
}
this.state.exprAllowed = true;
} else if (this.match(tt.slash) && prevType === tt.jsxTagStart) {
this.state.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
this.state.context.push(tc.j_cTag); // reconsider as closing tag context
this.state.exprAllowed = false;
} else {
return super.updateContext(prevType);
}
}
};

View File

@ -0,0 +1,258 @@
// @flow
const entities: { [name: string]: string } = {
quot: "\u0022",
amp: "&",
apos: "\u0027",
lt: "<",
gt: ">",
nbsp: "\u00A0",
iexcl: "\u00A1",
cent: "\u00A2",
pound: "\u00A3",
curren: "\u00A4",
yen: "\u00A5",
brvbar: "\u00A6",
sect: "\u00A7",
uml: "\u00A8",
copy: "\u00A9",
ordf: "\u00AA",
laquo: "\u00AB",
not: "\u00AC",
shy: "\u00AD",
reg: "\u00AE",
macr: "\u00AF",
deg: "\u00B0",
plusmn: "\u00B1",
sup2: "\u00B2",
sup3: "\u00B3",
acute: "\u00B4",
micro: "\u00B5",
para: "\u00B6",
middot: "\u00B7",
cedil: "\u00B8",
sup1: "\u00B9",
ordm: "\u00BA",
raquo: "\u00BB",
frac14: "\u00BC",
frac12: "\u00BD",
frac34: "\u00BE",
iquest: "\u00BF",
Agrave: "\u00C0",
Aacute: "\u00C1",
Acirc: "\u00C2",
Atilde: "\u00C3",
Auml: "\u00C4",
Aring: "\u00C5",
AElig: "\u00C6",
Ccedil: "\u00C7",
Egrave: "\u00C8",
Eacute: "\u00C9",
Ecirc: "\u00CA",
Euml: "\u00CB",
Igrave: "\u00CC",
Iacute: "\u00CD",
Icirc: "\u00CE",
Iuml: "\u00CF",
ETH: "\u00D0",
Ntilde: "\u00D1",
Ograve: "\u00D2",
Oacute: "\u00D3",
Ocirc: "\u00D4",
Otilde: "\u00D5",
Ouml: "\u00D6",
times: "\u00D7",
Oslash: "\u00D8",
Ugrave: "\u00D9",
Uacute: "\u00DA",
Ucirc: "\u00DB",
Uuml: "\u00DC",
Yacute: "\u00DD",
THORN: "\u00DE",
szlig: "\u00DF",
agrave: "\u00E0",
aacute: "\u00E1",
acirc: "\u00E2",
atilde: "\u00E3",
auml: "\u00E4",
aring: "\u00E5",
aelig: "\u00E6",
ccedil: "\u00E7",
egrave: "\u00E8",
eacute: "\u00E9",
ecirc: "\u00EA",
euml: "\u00EB",
igrave: "\u00EC",
iacute: "\u00ED",
icirc: "\u00EE",
iuml: "\u00EF",
eth: "\u00F0",
ntilde: "\u00F1",
ograve: "\u00F2",
oacute: "\u00F3",
ocirc: "\u00F4",
otilde: "\u00F5",
ouml: "\u00F6",
divide: "\u00F7",
oslash: "\u00F8",
ugrave: "\u00F9",
uacute: "\u00FA",
ucirc: "\u00FB",
uuml: "\u00FC",
yacute: "\u00FD",
thorn: "\u00FE",
yuml: "\u00FF",
OElig: "\u0152",
oelig: "\u0153",
Scaron: "\u0160",
scaron: "\u0161",
Yuml: "\u0178",
fnof: "\u0192",
circ: "\u02C6",
tilde: "\u02DC",
Alpha: "\u0391",
Beta: "\u0392",
Gamma: "\u0393",
Delta: "\u0394",
Epsilon: "\u0395",
Zeta: "\u0396",
Eta: "\u0397",
Theta: "\u0398",
Iota: "\u0399",
Kappa: "\u039A",
Lambda: "\u039B",
Mu: "\u039C",
Nu: "\u039D",
Xi: "\u039E",
Omicron: "\u039F",
Pi: "\u03A0",
Rho: "\u03A1",
Sigma: "\u03A3",
Tau: "\u03A4",
Upsilon: "\u03A5",
Phi: "\u03A6",
Chi: "\u03A7",
Psi: "\u03A8",
Omega: "\u03A9",
alpha: "\u03B1",
beta: "\u03B2",
gamma: "\u03B3",
delta: "\u03B4",
epsilon: "\u03B5",
zeta: "\u03B6",
eta: "\u03B7",
theta: "\u03B8",
iota: "\u03B9",
kappa: "\u03BA",
lambda: "\u03BB",
mu: "\u03BC",
nu: "\u03BD",
xi: "\u03BE",
omicron: "\u03BF",
pi: "\u03C0",
rho: "\u03C1",
sigmaf: "\u03C2",
sigma: "\u03C3",
tau: "\u03C4",
upsilon: "\u03C5",
phi: "\u03C6",
chi: "\u03C7",
psi: "\u03C8",
omega: "\u03C9",
thetasym: "\u03D1",
upsih: "\u03D2",
piv: "\u03D6",
ensp: "\u2002",
emsp: "\u2003",
thinsp: "\u2009",
zwnj: "\u200C",
zwj: "\u200D",
lrm: "\u200E",
rlm: "\u200F",
ndash: "\u2013",
mdash: "\u2014",
lsquo: "\u2018",
rsquo: "\u2019",
sbquo: "\u201A",
ldquo: "\u201C",
rdquo: "\u201D",
bdquo: "\u201E",
dagger: "\u2020",
Dagger: "\u2021",
bull: "\u2022",
hellip: "\u2026",
permil: "\u2030",
prime: "\u2032",
Prime: "\u2033",
lsaquo: "\u2039",
rsaquo: "\u203A",
oline: "\u203E",
frasl: "\u2044",
euro: "\u20AC",
image: "\u2111",
weierp: "\u2118",
real: "\u211C",
trade: "\u2122",
alefsym: "\u2135",
larr: "\u2190",
uarr: "\u2191",
rarr: "\u2192",
darr: "\u2193",
harr: "\u2194",
crarr: "\u21B5",
lArr: "\u21D0",
uArr: "\u21D1",
rArr: "\u21D2",
dArr: "\u21D3",
hArr: "\u21D4",
forall: "\u2200",
part: "\u2202",
exist: "\u2203",
empty: "\u2205",
nabla: "\u2207",
isin: "\u2208",
notin: "\u2209",
ni: "\u220B",
prod: "\u220F",
sum: "\u2211",
minus: "\u2212",
lowast: "\u2217",
radic: "\u221A",
prop: "\u221D",
infin: "\u221E",
ang: "\u2220",
and: "\u2227",
or: "\u2228",
cap: "\u2229",
cup: "\u222A",
int: "\u222B",
there4: "\u2234",
sim: "\u223C",
cong: "\u2245",
asymp: "\u2248",
ne: "\u2260",
equiv: "\u2261",
le: "\u2264",
ge: "\u2265",
sub: "\u2282",
sup: "\u2283",
nsub: "\u2284",
sube: "\u2286",
supe: "\u2287",
oplus: "\u2295",
otimes: "\u2297",
perp: "\u22A5",
sdot: "\u22C5",
lceil: "\u2308",
rceil: "\u2309",
lfloor: "\u230A",
rfloor: "\u230B",
lang: "\u2329",
rang: "\u232A",
loz: "\u25CA",
spades: "\u2660",
clubs: "\u2663",
hearts: "\u2665",
diams: "\u2666",
};
export default entities;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
// @flow
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
import { types as tt } from "./types";
import { lineBreak } from "../util/whitespace";
export class TokContext {
constructor(
token: string,
isExpr?: boolean,
preserveSpace?: boolean,
override?: Function, // Takes a Tokenizer as a this-parameter, and returns void.
) {
this.token = token;
this.isExpr = !!isExpr;
this.preserveSpace = !!preserveSpace;
this.override = override;
}
token: string;
isExpr: boolean;
preserveSpace: boolean;
override: ?Function;
}
export const types: {
[key: string]: TokContext,
} = {
braceStatement: new TokContext("{", false),
braceExpression: new TokContext("{", true),
templateQuasi: new TokContext("${", true),
parenStatement: new TokContext("(", false),
parenExpression: new TokContext("(", true),
template: new TokContext("`", true, true, p => p.readTmplToken()),
functionExpression: new TokContext("function", true),
};
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
if (this.state.context.length === 1) {
this.state.exprAllowed = true;
return;
}
const out = this.state.context.pop();
if (
out === types.braceStatement &&
this.curContext() === types.functionExpression
) {
this.state.context.pop();
this.state.exprAllowed = false;
} else if (out === types.templateQuasi) {
this.state.exprAllowed = true;
} else {
this.state.exprAllowed = !out.isExpr;
}
};
tt.name.updateContext = function(prevType) {
if (this.state.value === "of" && this.curContext() === types.parenStatement) {
this.state.exprAllowed = !prevType.beforeExpr;
return;
}
this.state.exprAllowed = false;
if (prevType === tt._let || prevType === tt._const || prevType === tt._var) {
if (lineBreak.test(this.input.slice(this.state.end))) {
this.state.exprAllowed = true;
}
}
};
tt.braceL.updateContext = function(prevType) {
this.state.context.push(
this.braceIsBlock(prevType) ? types.braceStatement : types.braceExpression,
);
this.state.exprAllowed = true;
};
tt.dollarBraceL.updateContext = function() {
this.state.context.push(types.templateQuasi);
this.state.exprAllowed = true;
};
tt.parenL.updateContext = function(prevType) {
const statementParens =
prevType === tt._if ||
prevType === tt._for ||
prevType === tt._with ||
prevType === tt._while;
this.state.context.push(
statementParens ? types.parenStatement : types.parenExpression,
);
this.state.exprAllowed = true;
};
tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
};
tt._function.updateContext = function() {
if (this.curContext() !== types.braceStatement) {
this.state.context.push(types.functionExpression);
}
this.state.exprAllowed = false;
};
tt.backQuote.updateContext = function() {
if (this.curContext() === types.template) {
this.state.context.pop();
} else {
this.state.context.push(types.template);
}
this.state.exprAllowed = false;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
// @flow
import type { Options } from "../options";
import * as N from "../types";
import { Position } from "../util/location";
import { types as ct, type TokContext } from "./context";
import type { Token } from "./index";
import { types as tt, type TokenType } from "./types";
export default class State {
init(options: Options, input: string): void {
this.strict =
options.strictMode === false ? false : options.sourceType === "module";
this.input = input;
this.potentialArrowAt = -1;
this.noArrowAt = [];
this.noArrowParamsConversionAt = [];
// eslint-disable-next-line max-len
this.inMethod = this.inFunction = this.inParameters = this.maybeInArrowParameters = this.inGenerator = this.inAsync = this.inPropertyName = this.inType = this.inClassProperty = this.noAnonFunctionType = false;
this.classLevel = 0;
this.labels = [];
this.decoratorStack = [[]];
this.yieldInPossibleArrowParameters = null;
this.tokens = [];
this.comments = [];
this.trailingComments = [];
this.leadingComments = [];
this.commentStack = [];
// $FlowIgnore
this.commentPreviousNode = null;
this.pos = this.lineStart = 0;
this.curLine = options.startLine;
this.type = tt.eof;
this.value = null;
this.start = this.end = this.pos;
this.startLoc = this.endLoc = this.curPosition();
// $FlowIgnore
this.lastTokEndLoc = this.lastTokStartLoc = null;
this.lastTokStart = this.lastTokEnd = this.pos;
this.context = [ct.braceStatement];
this.exprAllowed = true;
this.containsEsc = this.containsOctal = false;
this.octalPosition = null;
this.invalidTemplateEscapePosition = null;
this.exportedIdentifiers = [];
}
// TODO
strict: boolean;
// TODO
input: string;
// Used to signify the start of a potential arrow function
potentialArrowAt: number;
// Used to signify the start of an expression which looks like a
// typed arrow function, but it isn't
// e.g. a ? (b) : c => d
// ^
noArrowAt: number[];
// Used to signify the start of an expression whose params, if it looks like
// an arrow function, shouldn't be converted to assignable nodes.
// This is used to defer the validation of typed arrow functions inside
// conditional expressions.
// e.g. a ? (b) : c => d
// ^
noArrowParamsConversionAt: number[];
// Flags to track whether we are in a function, a generator.
inFunction: boolean;
inParameters: boolean;
maybeInArrowParameters: boolean;
inGenerator: boolean;
inMethod: boolean | N.MethodKind;
inAsync: boolean;
inType: boolean;
noAnonFunctionType: boolean;
inPropertyName: boolean;
inClassProperty: boolean;
// Check whether we are in a (nested) class or not.
classLevel: number;
// Labels in scope.
labels: Array<{ kind: ?("loop" | "switch"), statementStart?: number }>;
// Leading decorators. Last element of the stack represents the decorators in current context.
// Supports nesting of decorators, e.g. @foo(@bar class inner {}) class outer {}
// where @foo belongs to the outer class and @bar to the inner
decoratorStack: Array<Array<N.Decorator>>;
// The first yield expression inside parenthesized expressions and arrow
// function parameters. It is used to disallow yield in arrow function
// parameters.
yieldInPossibleArrowParameters: ?N.YieldExpression;
// Token store.
tokens: Array<Token | N.Comment>;
// Comment store.
comments: Array<N.Comment>;
// Comment attachment store
trailingComments: Array<N.Comment>;
leadingComments: Array<N.Comment>;
commentStack: Array<{
start: number,
leadingComments: ?Array<N.Comment>,
trailingComments: ?Array<N.Comment>,
}>;
commentPreviousNode: N.Node;
// The current position of the tokenizer in the input.
pos: number;
lineStart: number;
curLine: number;
// Properties of the current token:
// Its type
type: TokenType;
// For tokens that include more information than their type, the value
value: any;
// Its start and end offset
start: number;
end: number;
// And, if locations are used, the {line, column} object
// corresponding to those offsets
startLoc: Position;
endLoc: Position;
// Position information for the previous token
lastTokEndLoc: Position;
lastTokStartLoc: Position;
lastTokStart: number;
lastTokEnd: number;
// The context stack is used to superficially track syntactic
// context to predict whether a regular expression is allowed in a
// given position.
context: Array<TokContext>;
exprAllowed: boolean;
// Used to signal to callers of `readWord1` whether the word
// contained any escape sequences. This is needed because words with
// escape sequences must not be interpreted as keywords.
containsEsc: boolean;
// TODO
containsOctal: boolean;
octalPosition: ?number;
// Names of exports store. `default` is stored as a name for both
// `export default foo;` and `export { foo as default };`.
exportedIdentifiers: Array<string>;
invalidTemplateEscapePosition: ?number;
curPosition(): Position {
return new Position(this.curLine, this.pos - this.lineStart);
}
clone(skipArrays?: boolean): State {
const state = new State();
for (const key in this) {
// $FlowIgnore
let val = this[key];
if ((!skipArrays || key === "context") && Array.isArray(val)) {
val = val.slice();
}
// $FlowIgnore
state[key] = val;
}
return state;
}
}

View File

@ -0,0 +1,198 @@
// @flow
// ## Token types
// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.
// All token type variables start with an underscore, to make them
// easy to recognize.
// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.
const beforeExpr = true;
const startsExpr = true;
const isLoop = true;
const isAssign = true;
const prefix = true;
const postfix = true;
type TokenOptions = {
keyword?: string,
beforeExpr?: boolean,
startsExpr?: boolean,
rightAssociative?: boolean,
isLoop?: boolean,
isAssign?: boolean,
prefix?: boolean,
postfix?: boolean,
binop?: ?number,
};
export class TokenType {
label: string;
keyword: ?string;
beforeExpr: boolean;
startsExpr: boolean;
rightAssociative: boolean;
isLoop: boolean;
isAssign: boolean;
prefix: boolean;
postfix: boolean;
binop: ?number;
updateContext: ?(prevType: TokenType) => void;
constructor(label: string, conf: TokenOptions = {}) {
this.label = label;
this.keyword = conf.keyword;
this.beforeExpr = !!conf.beforeExpr;
this.startsExpr = !!conf.startsExpr;
this.rightAssociative = !!conf.rightAssociative;
this.isLoop = !!conf.isLoop;
this.isAssign = !!conf.isAssign;
this.prefix = !!conf.prefix;
this.postfix = !!conf.postfix;
this.binop = conf.binop === 0 ? 0 : conf.binop || null;
this.updateContext = null;
}
}
class KeywordTokenType extends TokenType {
constructor(name: string, options: TokenOptions = {}) {
options.keyword = name;
super(name, options);
}
}
export class BinopTokenType extends TokenType {
constructor(name: string, prec: number) {
super(name, { beforeExpr, binop: prec });
}
}
export const types: { [name: string]: TokenType } = {
num: new TokenType("num", { startsExpr }),
bigint: new TokenType("bigint", { startsExpr }),
regexp: new TokenType("regexp", { startsExpr }),
string: new TokenType("string", { startsExpr }),
name: new TokenType("name", { startsExpr }),
eof: new TokenType("eof"),
// Punctuation token types.
bracketL: new TokenType("[", { beforeExpr, startsExpr }),
bracketR: new TokenType("]"),
braceL: new TokenType("{", { beforeExpr, startsExpr }),
braceBarL: new TokenType("{|", { beforeExpr, startsExpr }),
braceR: new TokenType("}"),
braceBarR: new TokenType("|}"),
parenL: new TokenType("(", { beforeExpr, startsExpr }),
parenR: new TokenType(")"),
comma: new TokenType(",", { beforeExpr }),
semi: new TokenType(";", { beforeExpr }),
colon: new TokenType(":", { beforeExpr }),
doubleColon: new TokenType("::", { beforeExpr }),
dot: new TokenType("."),
question: new TokenType("?", { beforeExpr }),
questionDot: new TokenType("?."),
arrow: new TokenType("=>", { beforeExpr }),
template: new TokenType("template"),
ellipsis: new TokenType("...", { beforeExpr }),
backQuote: new TokenType("`", { startsExpr }),
dollarBraceL: new TokenType("${", { beforeExpr, startsExpr }),
at: new TokenType("@"),
hash: new TokenType("#"),
// Operators. These carry several kinds of properties to help the
// parser use them properly (the presence of these properties is
// what categorizes them as operators).
//
// `binop`, when present, specifies that this operator is a binary
// operator, and will refer to its precedence.
//
// `prefix` and `postfix` mark the operator as a prefix or postfix
// unary operator.
//
// `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
// binary operators with a very low precedence, that should result
// in AssignmentExpression nodes.
eq: new TokenType("=", { beforeExpr, isAssign }),
assign: new TokenType("_=", { beforeExpr, isAssign }),
incDec: new TokenType("++/--", { prefix, postfix, startsExpr }),
bang: new TokenType("!", { beforeExpr, prefix, startsExpr }),
tilde: new TokenType("~", { beforeExpr, prefix, startsExpr }),
pipeline: new BinopTokenType("|>", 0),
nullishCoalescing: new BinopTokenType("??", 1),
logicalOR: new BinopTokenType("||", 1),
logicalAND: new BinopTokenType("&&", 2),
bitwiseOR: new BinopTokenType("|", 3),
bitwiseXOR: new BinopTokenType("^", 4),
bitwiseAND: new BinopTokenType("&", 5),
equality: new BinopTokenType("==/!=", 6),
relational: new BinopTokenType("</>", 7),
bitShift: new BinopTokenType("<</>>", 8),
plusMin: new TokenType("+/-", { beforeExpr, binop: 9, prefix, startsExpr }),
modulo: new BinopTokenType("%", 10),
star: new BinopTokenType("*", 10),
slash: new BinopTokenType("/", 10),
exponent: new TokenType("**", {
beforeExpr,
binop: 11,
rightAssociative: true,
}),
};
export const keywords = {
break: new KeywordTokenType("break"),
case: new KeywordTokenType("case", { beforeExpr }),
catch: new KeywordTokenType("catch"),
continue: new KeywordTokenType("continue"),
debugger: new KeywordTokenType("debugger"),
default: new KeywordTokenType("default", { beforeExpr }),
do: new KeywordTokenType("do", { isLoop, beforeExpr }),
else: new KeywordTokenType("else", { beforeExpr }),
finally: new KeywordTokenType("finally"),
for: new KeywordTokenType("for", { isLoop }),
function: new KeywordTokenType("function", { startsExpr }),
if: new KeywordTokenType("if"),
return: new KeywordTokenType("return", { beforeExpr }),
switch: new KeywordTokenType("switch"),
throw: new KeywordTokenType("throw", { beforeExpr, prefix, startsExpr }),
try: new KeywordTokenType("try"),
var: new KeywordTokenType("var"),
let: new KeywordTokenType("let"),
const: new KeywordTokenType("const"),
while: new KeywordTokenType("while", { isLoop }),
with: new KeywordTokenType("with"),
new: new KeywordTokenType("new", { beforeExpr, startsExpr }),
this: new KeywordTokenType("this", { startsExpr }),
super: new KeywordTokenType("super", { startsExpr }),
class: new KeywordTokenType("class"),
extends: new KeywordTokenType("extends", { beforeExpr }),
export: new KeywordTokenType("export"),
import: new KeywordTokenType("import", { startsExpr }),
yield: new KeywordTokenType("yield", { beforeExpr, startsExpr }),
null: new KeywordTokenType("null", { startsExpr }),
true: new KeywordTokenType("true", { startsExpr }),
false: new KeywordTokenType("false", { startsExpr }),
in: new KeywordTokenType("in", { beforeExpr, binop: 7 }),
instanceof: new KeywordTokenType("instanceof", { beforeExpr, binop: 7 }),
typeof: new KeywordTokenType("typeof", { beforeExpr, prefix, startsExpr }),
void: new KeywordTokenType("void", { beforeExpr, prefix, startsExpr }),
delete: new KeywordTokenType("delete", { beforeExpr, prefix, startsExpr }),
};
// Map keyword names to token types.
Object.keys(keywords).forEach(name => {
types["_" + name] = keywords[name];
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
/* eslint max-len: 0 */
// @flow
function makePredicate(words: string): (str: string) => boolean {
const wordsArr = words.split(" ");
return function(str) {
return wordsArr.indexOf(str) >= 0;
};
}
// Reserved word lists for various dialects of the language
export const reservedWords = {
"6": makePredicate("enum await"),
strict: makePredicate(
"implements interface let package private protected public static yield",
),
strictBind: makePredicate("eval arguments"),
};
// And the keywords
export const isKeyword = makePredicate(
"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this let const class extends export import yield super",
);
// ## Character categories
// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.
let nonASCIIidentifierStartChars =
"\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ae\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab65\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
let nonASCIIidentifierChars =
"\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
const nonASCIIidentifierStart = new RegExp(
"[" + nonASCIIidentifierStartChars + "]",
);
const nonASCIIidentifier = new RegExp(
"[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]",
);
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by `bin/generate-identifier-regex.js`.
// eslint-disable-next-line comma-spacing
/* prettier-ignore */ const astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,785,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,54,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,86,25,391,63,32,0,449,56,264,8,2,36,18,0,50,29,881,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,65,0,32,6124,20,754,9486,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,60,67,1213,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,10591,541];
// eslint-disable-next-line comma-spacing
/* prettier-ignore */ const astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,10,2,4,9,83,11,7,0,161,11,6,9,7,3,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,87,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,423,9,838,7,2,7,17,9,57,21,2,13,19882,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,2214,6,110,6,6,9,792487,239];
// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code: number, set: $ReadOnlyArray<number>): boolean {
let pos = 0x10000;
for (let i = 0; i < set.length; i += 2) {
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];
if (pos >= code) return true;
}
return false;
}
// Test whether a given character code starts an identifier.
export function isIdentifierStart(code: number): boolean {
if (code < 65) return code === 36;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff)
return (
code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code))
);
return isInAstralSet(code, astralIdentifierStartCodes);
}
// Test whether a given character is part of an identifier.
export function isIdentifierChar(code: number): boolean {
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff)
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
return (
isInAstralSet(code, astralIdentifierStartCodes) ||
isInAstralSet(code, astralIdentifierCodes)
);
}

View File

@ -0,0 +1,54 @@
// @flow
import { lineBreakG } from "./whitespace";
export type Pos = {
start: number,
};
// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.
export class Position {
line: number;
column: number;
constructor(line: number, col: number) {
this.line = line;
this.column = col;
}
}
export class SourceLocation {
start: Position;
end: Position;
filename: string;
identifierName: ?string;
constructor(start: Position, end?: Position) {
this.start = start;
// $FlowIgnore (may start as null, but initialized later)
this.end = end;
}
}
// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.
export function getLineInfo(input: string, offset: number): Position {
for (let line = 1, cur = 0; ; ) {
lineBreakG.lastIndex = cur;
const match = lineBreakG.exec(input);
if (match && match.index < offset) {
++line;
cur = match.index + match[0].length;
} else {
return new Position(line, offset - cur);
}
}
// istanbul ignore next
throw new Error("Unreachable");
}

View File

@ -0,0 +1,13 @@
// @flow
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
export const lineBreak = /\r\n?|\n|\u2028|\u2029/;
export const lineBreakG = new RegExp(lineBreak.source, "g");
export function isNewLine(code: number): boolean {
return code === 10 || code === 13 || code === 0x2028 || code === 0x2029;
}
export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;

View File

@ -0,0 +1,5 @@
import path from "path";
import { runThrowTestsWithEstree } from "./helpers/runFixtureTests";
import { parse } from "../lib";
runThrowTestsWithEstree(path.join(__dirname, "fixtures"), parse);

View File

@ -0,0 +1,5 @@
import path from "path";
import { runFixtureTests } from "./helpers/runFixtureTests";
import { parseExpression } from "../lib";
runFixtureTests(path.join(__dirname, "expressions"), parseExpression);

View File

@ -0,0 +1,21 @@
Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,50 @@
{
"type": "BinaryExpression",
"start": 0,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 5
}
},
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"operator": "+",
"right": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "y"
},
"name": "y"
}
}

View File

@ -0,0 +1,50 @@
{
"type": "BinaryExpression",
"start": 0,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 5
}
},
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"operator": "-",
"right": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "y"
},
"name": "y"
}
}

View File

@ -0,0 +1 @@
"use strict" + 42

View File

@ -0,0 +1,56 @@
{
"type": "BinaryExpression",
"start": 0,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 17
}
},
"left": {
"type": "StringLiteral",
"start": 0,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 12
}
},
"extra": {
"rawValue": "use strict",
"raw": "\"use strict\""
},
"value": "use strict"
},
"operator": "+",
"right": {
"type": "NumericLiteral",
"start": 15,
"end": 17,
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 17
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 6
}
},
"operator": "=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 4,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 6
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
}
},
"operator": "=",
"left": {
"type": "Identifier",
"start": 0,
"end": 4,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 4
},
"identifierName": "eval"
},
"name": "eval"
},
"right": {
"type": "NumericLiteral",
"start": 7,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 9
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 14
}
},
"operator": "=",
"left": {
"type": "Identifier",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
},
"identifierName": "arguments"
},
"name": "arguments"
},
"right": {
"type": "NumericLiteral",
"start": 12,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 14
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "*=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "/=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "%=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "+=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "-=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"operator": "<<=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 6,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 8
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"operator": ">>=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 6,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 8
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 9
}
},
"operator": ">>>=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 7,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 9
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "&=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "^=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,53 @@
{
"type": "AssignmentExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"operator": "|=",
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"right": {
"type": "NumericLiteral",
"start": 5,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 7
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}

View File

@ -0,0 +1,50 @@
{
"type": "BinaryExpression",
"start": 0,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 5
}
},
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"operator": "&",
"right": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "y"
},
"name": "y"
}
}

View File

@ -0,0 +1,50 @@
{
"type": "BinaryExpression",
"start": 0,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 5
}
},
"left": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
},
"identifierName": "x"
},
"name": "x"
},
"operator": "^",
"right": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4
},
"end": {
"line": 1,
"column": 5
},
"identifierName": "y"
},
"name": "y"
}
}

Some files were not shown because too many files have changed in this diff Show More