diff --git a/.babelignore b/.babelignore
deleted file mode 100644
index ec46d9b5e8..0000000000
--- a/.babelignore
+++ /dev/null
@@ -1,2 +0,0 @@
-test/fixtures
-lib
diff --git a/.babelrc b/.babelrc
index 545548e0d7..7717ac3939 100644
--- a/.babelrc
+++ b/.babelrc
@@ -7,8 +7,7 @@
"stage-0"
],
"plugins": [
- "transform-flow-strip-types",
- "external-helpers"
+ "transform-flow-strip-types"
],
"env": {
"watch": {
diff --git a/.flowconfig b/.flowconfig
index 3dccffe4e4..ec70beff84 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -11,4 +11,3 @@
strip_root=true
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue
-suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
diff --git a/.travis.yml b/.travis.yml
index eb22eaf9c0..e1d38de890 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,43 +1,48 @@
sudo: false
language: node_js
node_js:
+ - "0.10"
+ - "0.12"
- "4"
+ - "5"
- "6"
- "7"
-env:
- - JOB=test
+before_install:
+ # Rollup doesn't support node < 4.x. Switch to latest for build
+ - . $HOME/.nvm/nvm.sh
+ - nvm install stable && nvm use stable
before_script:
- - 'if [ "$JOB" = "babel-test" ]; then make bootstrap-babel ; fi'
- - 'if [ "$JOB" = "flow-test" ]; then make bootstrap-flow ; fi'
- - 'if [ "$JOB" = "test" ]; then yarn run build; fi'
+ - 'if [ -n "${BABEL-}" ]; then make bootstrap-babel ; fi'
+ - 'if [ -n "${FLOWTESTS-}" ]; then make bootstrap-flow ; fi'
+ - 'BABEL_ENV=test npm run build'
+ # Switch back to node version currently being tested prior to test run
+ - 'nvm use $TRAVIS_NODE_VERSION;'
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" = "test-coverage" ]; then yarn run test-coverage; fi'
+ - 'if [ -n "${LINT-}" ]; then npm run lint ; fi'
+ - 'if [ -n "${FLOW-}" ]; then npm run flow ; fi'
+ - 'if [ -n "${FLOWTESTS-}" ]; then make test-flow ; fi'
+ - 'if [ -n "${BABEL-}" ]; then make test-babel ; fi'
+ - 'if [ -z "${LINT-}" ] && [ -z "${FLOW-}" ] && [ -z "${BABEL-}" ] && [ -z "${FLOWTESTS-}" ]; then npm run test-ci ; 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: LINT=true
+ - node_js: "node"
+ env: FLOW=true
+ - node_js: "node"
+ env: BABEL=true
+ - node_js: "node"
+ env: FLOWTESTS=true
allow_failures:
- - node_js: "lts/*"
- env: JOB=flow-test
+ - node_js: "node"
+ env: FLOWTESTS=true
-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'
+after_success: 'if [ -z "${LINT-}" ] && [ -z "${FLOW-}" ] && [ -z "${FLOWTESTS-}" ]; then npm run coverage ; fi'
notifications:
slack: babeljs:5Wy4QX13KVkGy9CnU0rmvgeK
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7a486500a..bb482fc5c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,125 +15,6 @@ _Note: Gaps between patch versions are faulty, broken or test releases._
See the [Babel Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md) for the pre-6.8.0 version Changelog.
-## v6.17.0 (2017-04-20)
-
-### :bug: Bug Fix
- * Cherry-pick #418 to 6.x ([#476](https://github.com/babel/babylon/pull/476)) (Sebastian McKenzie)
- * Add support for invalid escapes in tagged templates ([#274](https://github.com/babel/babylon/pull/274)) (Kevin Gibbons)
- * Throw error if new.target is used outside of a function ([#402](https://github.com/babel/babylon/pull/402)) (Brian Ng)
- * Fix parsing of class properties ([#351](https://github.com/babel/babylon/pull/351)) (Kevin Gibbons)
- * Fix parsing yield with dynamicImport ([#383](https://github.com/babel/babylon/pull/383)) (Brian Ng)
- * Ensure consistent start args for parseParenItem ([#386](https://github.com/babel/babylon/pull/386)) (Brian Ng)
-
-## 7.0.0-beta.8 (2017-04-04)
-
-### New Feature
-* Add support for flow type spread (#418) (Conrad Buck)
-* Allow statics in flow interfaces (#427) (Brian Ng)
-
-### Bug Fix
-* Fix predicate attachment to match flow parser (#428) (Brian Ng)
-* Add extra.raw back to JSXText and JSXAttribute (#344) (Alex Rattray)
-* Fix rest parameters with array and objects (#424) (Brian Ng)
-* Fix number parser (#433) (Alex Kuzmenko)
-
-### Docs
-* Fix CONTRIBUTING.md [skip ci] (#432) (Alex Kuzmenko)
-
-### Internal
-* Use babel-register script when running babel smoke tests (#442) (Brian Ng)
-
-## 7.0.0-beta.7 (2017-03-22)
-
-### Spec Compliancy
-* Remove babylon plugin for template revision since it's stage-4 (#426) (Henry Zhu)
-
-### Bug Fix
-
-* Fix push-pop logic in flow (#405) (Daniel Tschinder)
-
-## 7.0.0-beta.6 (2017-03-21)
-
-### New Feature
-* Add support for invalid escapes in tagged templates (#274) (Kevin Gibbons)
-
-### Polish
-* Improves error message when super is called outside of constructor (#408) (Arshabh Kumar Agarwal)
-
-### Docs
-
-* [7.0] Moved value field in spec from ObjectMember to ObjectProperty as ObjectMethod's don't have it (#415) [skip ci] (James Browning)
-
-## 7.0.0-beta.5 (2017-03-21)
-
-### Bug Fix
-* Throw error if new.target is used outside of a function (#402) (Brian Ng)
-* Fix parsing of class properties (#351) (Kevin Gibbons)
-
-### Other
- * Test runner: Detect extra property in 'actual' but not in 'expected'. (#407) (Andy)
- * Optimize travis builds (#419) (Daniel Tschinder)
- * Update codecov to 2.0 (#412) (Daniel Tschinder)
- * Fix spec for ClassMethod: It doesn't have a function, it *is* a function. (#406) [skip ci] (Andy)
- * Changed Non-existent RestPattern to RestElement which is what is actually parsed (#409) [skip ci] (James Browning)
- * Upgrade flow to 0.41 (Daniel Tschinder)
- * Fix watch command (#403) (Brian Ng)
- * Update yarn lock (Daniel Tschinder)
- * Fix watch command (#403) (Brian Ng)
- * chore(package): update flow-bin to version 0.41.0 (#395) (greenkeeper[bot])
- * Add estree test for correct order of directives (Daniel Tschinder)
- * Add DoExpression to spec (#364) (Alex Kuzmenko)
- * Mention cloning of repository in CONTRIBUTING.md (#391) [skip ci] (Sumedh Nimkarde)
- * Explain how to run only one test (#389) [skip ci] (Aaron Ang)
-
- ## 7.0.0-beta.4 (2017-03-01)
-
-* Don't consume async when checking for async func decl (#377) (Brian Ng)
-* add `ranges` option [skip ci] (Henry Zhu)
-* Don't parse class properties without initializers when classProperties is disabled and Flow is enabled (#300) (Andrew Levine)
-
-## 7.0.0-beta.3 (2017-02-28)
-
-- [7.0] Change RestProperty/SpreadProperty to RestElement/SpreadElement (#384)
-- Merge changes from 6.x
-
-## 7.0.0-beta.2 (2017-02-20)
-
-- estree: correctly change literals in all cases (#368) (Daniel Tschinder)
-
-## 7.0.0-beta.1 (2017-02-20)
-
-- Fix negative number literal typeannotations (#366) (Daniel Tschinder)
-- Update contributing with more test info [skip ci] (#355) (Brian Ng)
-
-## 7.0.0-beta.0 (2017-02-15)
-
-- Reintroduce Variance node (#333) (Daniel Tschinder)
-- Rename NumericLiteralTypeAnnotation to NumberLiteralTypeAnnotation (#332) (Charles Pick)
-- [7.0] Remove ForAwaitStatement, add await flag to ForOfStatement (#349) (Brandon Dail)
-- chore(package): update ava to version 0.18.0 (#345) (greenkeeper[bot])
-- chore(package): update babel-plugin-istanbul to version 4.0.0 (#350) (greenkeeper[bot])
-- Change location of ObjectTypeIndexer to match flow (#228) (Daniel Tschinder)
-- Rename flow AST Type ExistentialTypeParam to ExistsTypeAnnotation (#322) (Toru Kobayashi)
-- Revert "Temporary rollback for erroring on trailing comma with spread (#154)" (#290) (Daniel Tschinder)
-- Remove classConstructorCall plugin (#291) (Brian Ng)
-- Update yarn.lock (Daniel Tschinder)
-- Update cross-env to 3.x (Daniel Tschinder)
-- [7.0] Remove node 0.10, 0.12 and 5 from Travis (#284) (Sergey Rubanov)
-- Remove `String.fromCodePoint` shim (#279) (Mathias Bynens)
-
-## 6.16.1 (2017-02-23)
-
-### :bug: Regression
-
-- Revert "Fix export default async function to be FunctionDeclaration" ([#375](https://github.com/babel/babylon/pull/375))
-
-Need to modify Babel for this AST node change, so moving to 7.0.
-
-- Revert "Don't parse class properties without initializers when classProperties plugin is disabled, and Flow is enabled" ([#376](https://github.com/babel/babylon/pull/376))
-
-[react-native](https://github.com/facebook/react-native/issues/12542) broke with this so we reverted.
-
## 6.16.0 (2017-02-23)
### :rocket: New Feature
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index eefd2e2706..5130b2c078 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,14 +6,10 @@ contributing, please read the
## 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
+npm install
```
## Tests
@@ -23,45 +19,25 @@ yarn
To run a build, tests and perform lint/flow checks:
```bash
-yarn test
+npm test
```
If you only want to run the tests:
```bash
-yarn run test-only
+npm run test-only
```
-Note, this does not actually run a build, so you may have to call `yarn run build` after
+Note, this does not actually run a build, so you may have to call `npm 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
+BABEL_ENV=test npm run build && npm run test-ci
```
### Writing tests
@@ -85,21 +61,19 @@ you will want to link both repositories together. This can be done by doing the
```bash
cd babylon/
-yarn link
-yarn run build
+npm link
+npm run build
cd ../babel/
make bootstrap
-yarn link babylon
+npm 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
+npm link babylon
+cd ../../packages/babel-template/
+npm link babylon
+cd ../../packages/babel-traverse/
+npm link babylon
+cd ../../packages/babel-generator/
+npm link babylon
cd ../..
make build
make test
diff --git a/Makefile b/Makefile
index 351fc07848..22e088f90a 100644
--- a/Makefile
+++ b/Makefile
@@ -8,18 +8,19 @@ clean: ; rm -rf ./build
bootstrap-babel: clean
mkdir ./build
- git clone --depth=1 --branch=7.0 https://github.com/babel/babel.git ./build/babel
+ 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
+ BABEL_ENV=test npm 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; \
+ ../../node_modules/.bin/nyc --no-instrument --no-source-map --report-dir ../../coverage node_modules/mocha/bin/_mocha `scripts/_get-test-directories.sh` --opts test/mocha.opts; \
+ mv .nyc_output ../../.nyc_output
bootstrap-flow: clean
mkdir ./build
diff --git a/README.md b/README.md
index 3aaf8acf43..65ee78a8f6 100644
--- a/README.md
+++ b/README.md
@@ -1,137 +1,20 @@
-
-
-
+# babylon-lightscript
-
- Babylon is a JavaScript parser used in Babel.
-
+The parser for [LightScript](http://lightscript.org), a concise syntax for JavaScript.
-
-
-
-
-
+To be used through [babel-plugin-lightscript](https://github.com/lightscript/babel-plugin-lightscript).
- - The latest ECMAScript version enabled by default (ES2017).
- - Comment attachment.
- - Support for JSX and Flow.
- - Support for experimental language proposals (accepting PRs for anything at least [stage-0](https://github.com/tc39/proposals/blob/master/stage-0-proposals.md)).
+A minimally-invasive fork of [Babylon](https://github.com/babel/babylon).
+With the exception of a few reserved keywords,
+it parses JS as JS unless the `"lightscript"` plugin is passed.
-## 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).
+### Contributing
-Significant diversions are expected to occur in the future such as streaming, EBNF definitions, sweet.js integration, interspatial parsing and more.
-
-## 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]`
-
-### 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
-
- - `estree`
- - `jsx`
- - `flow`
- - `doExpressions`
- - `objectRestSpread`
- - `decorators` (Based on an outdated version of the Decorators proposal. Will be removed in a future version of `Babylon`)
- - `classProperties`
- - `exportExtensions`
- - `asyncGenerators`
- - `functionBind`
- - `functionSent`
- - `dynamicImport`
+ yarn
+ yarn build
+ yarn test
+New tests should go in the
+[lightscript](https://github.com/lightscript/babylon-lightscript/tree/lightscript/test/fixtures/lightscript)
+directory.
diff --git a/ast/spec.md b/ast/spec.md
index 0ff1c87c51..9e2e094b9f 100644
--- a/ast/spec.md
+++ b/ast/spec.md
@@ -1,7 +1,6 @@
These are the core Babylon AST node types.
- [Node objects](#node-objects)
-- [Changes](#changes)
- [Identifier](#identifier)
- [Literals](#literals)
- [RegExpLiteral](#regexpliteral)
@@ -36,6 +35,7 @@ These are the core Babylon AST node types.
- [ForStatement](#forstatement)
- [ForInStatement](#forinstatement)
- [ForOfStatement](#forofstatement)
+ - [ForAwaitStatement](#forawaitstatement)
- [Declarations](#declarations)
- [FunctionDeclaration](#functiondeclaration)
- [VariableDeclaration](#variabledeclaration)
@@ -56,6 +56,8 @@ These are the core Babylon AST node types.
- [ObjectMember](#objectmember)
- [ObjectProperty](#objectproperty)
- [ObjectMethod](#objectmethod)
+ - [RestProperty](#restproperty)
+ - [SpreadProperty](#spreadproperty)
- [FunctionExpression](#functionexpression)
- [Unary operations](#unary-operations)
- [UnaryExpression](#unaryexpression)
@@ -76,7 +78,6 @@ These are the core Babylon AST node types.
- [CallExpression](#callexpression)
- [NewExpression](#newexpression)
- [SequenceExpression](#sequenceexpression)
- - [DoExpression](#doexpression)
- [Template Literals](#template-literals)
- [TemplateLiteral](#templateliteral)
- [TaggedTemplateExpression](#taggedtemplateexpression)
@@ -139,22 +140,6 @@ interface Position {
}
```
-# Changes
-
-### Babylon 7
-
-Flow: Node renamed from `ExistentialTypeParam` to `ExistsTypeAnnotation` [#322](https://github.com/babel/babylon/pull/322)
-
-Flow: Node renamed from `NumericLiteralTypeAnnotation` to `NumberLiteralTypeAnnotation` [babel/babylon#332](https://github.com/babel/babylon/pull/332)
-
-Flow: Node `Variance` which replaces the string value of the `variance` field on several nodes [babel/babylon#333](https://github.com/babel/babylon/pull/333)
-
-Flow: `ObjectTypeIndexer` location info matches Flow's better [babel/babylon#228](https://github.com/babel/babylon/pull/228)
-
-Node `ForAwaitStatement` has been removed [#349](https://github.com/babel/babylon/pull/349) in favor of modifying `ForOfStatement`
-
-`RestProperty` and `SpreadProperty` have been dropped in favor of `RestElement` and `SpreadElement`.
-
# Identifier
```js
@@ -493,7 +478,16 @@ A `for`/`in` statement.
```js
interface ForOfStatement <: ForInStatement {
type: "ForOfStatement";
- await: boolean;
+}
+```
+
+A `for`/`await` statement.
+
+## ForAwaitStatement
+
+```js
+interface ForAwaitStatement <: ForInStatement {
+ type: "ForAwaitStatement";
}
```
@@ -657,7 +651,7 @@ An array expression.
```js
interface ObjectExpression <: Expression {
type: "ObjectExpression";
- properties: [ ObjectProperty | ObjectMethod | SpreadElement ];
+ properties: [ ObjectProperty | ObjectMethod | SpreadProperty ];
}
```
@@ -669,6 +663,7 @@ An object expression.
interface ObjectMember <: Node {
key: Expression;
computed: boolean;
+ value: Expression;
decorators: [ Decorator ];
}
```
@@ -679,7 +674,6 @@ interface ObjectMember <: Node {
interface ObjectProperty <: ObjectMember {
type: "ObjectProperty";
shorthand: boolean;
- value: Expression;
}
```
@@ -692,6 +686,24 @@ interface ObjectMethod <: ObjectMember, Function {
}
```
+## RestProperty
+
+```js
+interface RestProperty <: Node {
+ type: "RestProperty";
+ argument: Expression;
+}
+```
+
+## SpreadProperty
+
+```js
+interface SpreadProperty <: Node {
+ type: "SpreadProperty";
+ argument: Expression;
+}
+```
+
## FunctionExpression
```js
@@ -908,15 +920,6 @@ interface SequenceExpression <: Expression {
A sequence expression, i.e., a comma-separated sequence of expressions.
-## DoExpression
-
-```js
-interface DoExpression <: Expression {
- type: "DoExpression";
- body: BlockStatement
-}
-```
-
# Template Literals
## TemplateLiteral
@@ -946,7 +949,7 @@ interface TemplateElement <: Node {
type: "TemplateElement";
tail: boolean;
value: {
- cooked: string | null;
+ cooked: string;
raw: string;
};
}
@@ -967,7 +970,7 @@ interface AssignmentProperty <: ObjectProperty {
interface ObjectPattern <: Pattern {
type: "ObjectPattern";
- properties: [ AssignmentProperty | RestElement ];
+ properties: [ AssignmentProperty | RestProperty ];
}
```
@@ -1022,9 +1025,10 @@ interface ClassBody <: Node {
## ClassMethod
```js
-interface ClassMethod <: Function {
+interface ClassMethod <: Node {
type: "ClassMethod";
key: Expression;
+ value: FunctionExpression;
kind: "constructor" | "method" | "get" | "set";
computed: boolean;
static: boolean;
diff --git a/bin/babylon-lightscript.js b/bin/babylon-lightscript.js
new file mode 100755
index 0000000000..b74fdc7e0d
--- /dev/null
+++ b/bin/babylon-lightscript.js
@@ -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, { plugins: ["lightscript"] });
+
+console.log(JSON.stringify(ast, null, " "));
diff --git a/package.json b/package.json
index 3280a72a9a..7534fceb0f 100644
--- a/package.json
+++ b/package.json
@@ -1,64 +1,64 @@
{
- "name": "babylon",
- "version": "7.0.0-beta.8",
- "description": "A JavaScript parser",
- "author": "Sebastian McKenzie ",
- "homepage": "https://babeljs.io/",
+ "name": "babylon-lightscript",
+ "version": "0.5.9",
+ "description": "A LightScript parser, based on babylon (a JavaScript parser)",
+ "author": "Alex Rattray ",
+ "homepage": "http://lightscript.org/",
"license": "MIT",
"keywords": [
"babel",
"javascript",
"parser",
- "babylon"
+ "babylon",
+ "lightscript"
],
- "repository": "https://github.com/babel/babylon",
+ "repository": "https://github.com/lightscript/babylon-lightscript",
"main": "lib/index.js",
"files": [
"bin",
"lib"
],
- "engines": {
- "node": ">=4.2.0"
- },
"devDependencies": {
- "ava": "^0.19.0",
- "babel-cli": "^7.0.0-alpha.6",
+ "ava": "^0.17.0",
+ "babel-cli": "^6.14.0",
"babel-eslint": "^7.0.0",
- "babel-helper-fixtures": "^7.0.0-alpha.3",
- "babel-plugin-external-helpers": "^7.0.0-alpha.3",
- "babel-plugin-istanbul": "^4.0.0",
- "babel-plugin-transform-flow-strip-types": "^7.0.0-alpha.3",
- "babel-preset-es2015": "^7.0.0-alpha.3",
- "babel-preset-stage-0": "^7.0.0-alpha.3",
+ "babel-helper-fixtures": "^6.9.0",
+ "babel-plugin-istanbul": "^3.0.0",
+ "babel-plugin-transform-flow-strip-types": "^6.14.0",
+ "babel-preset-es2015": "^6.14.0",
+ "babel-preset-stage-0": "^6.5.0",
"chalk": "^1.1.3",
- "cross-env": "^4.0.0",
+ "codecov": "^1.0.1",
+ "cross-env": "^2.0.0",
"eslint": "^3.7.1",
"eslint-config-babel": "^6.0.0",
"eslint-plugin-flowtype": "^2.20.0",
- "flow-bin": "^0.44.0",
+ "flow-bin": "^0.40.0",
"nyc": "^10.0.0",
"rimraf": "^2.5.4",
"rollup": "^0.41.0",
"rollup-plugin-babel": "^2.6.1",
- "rollup-plugin-node-resolve": "^3.0.0",
- "rollup-watch": "^3.2.2",
+ "rollup-plugin-node-resolve": "^2.0.0",
+ "try-resolve": "^1.0.1",
"unicode-9.0.0": "~0.7.0"
},
"bin": {
- "babylon": "./bin/babylon.js"
+ "babylon": "./bin/babylon.js",
+ "babylon-lightscript": "./bin/babylon-lightscript.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'",
+ "build": "npm run clean && rollup -c",
+ "coverage": "nyc report --reporter=json && codecov -f coverage/coverage-final.json",
+ "lint": "eslint src bin",
"clean": "rimraf lib",
"flow": "flow",
- "lint": "eslint src bin",
- "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",
+ "prepublish": "cross-env BABEL_ENV=production npm run build",
+ "preversion": "npm run test",
+ "test": "npm run lint && npm run flow && npm run build -- -m && npm 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"
+ "test-ci": "nyc npm run test-only",
+ "changelog": "git log `git describe --tags --abbrev=0`..HEAD --pretty=format:' * %s (%an)' | grep -v 'Merge pull request'",
+ "watch": "npm run clean && cross-env BABEL_ENV=watch babel src --out-dir lib --watch"
},
"nyc": {
"include": [
@@ -76,5 +76,10 @@
"src/**/*.js",
"bin/**/*.js"
]
+ },
+ "greenkeeper": {
+ "ignore": [
+ "cross-env"
+ ]
}
}
diff --git a/scripts/rmExpected.js b/scripts/rmExpected.js
deleted file mode 100644
index 613071b02f..0000000000
--- a/scripts/rmExpected.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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);
- }
- }
- }
-}
diff --git a/src/index.js b/src/index.js
index 09a5c40cbe..7e4285c809 100755
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,3 @@
-// @flow
-
-import type { Options } from "./options";
import Parser, { plugins } from "./parser";
import "./parser/util";
import "./parser/statement";
@@ -14,21 +11,21 @@ import { types as tokTypes } from "./tokenizer/types";
import "./tokenizer";
import "./tokenizer/context";
-import type { Expression, File } from "./types";
-
+import lightscriptPlugin from "./plugins/lightscript";
import estreePlugin from "./plugins/estree";
import flowPlugin from "./plugins/flow";
import jsxPlugin from "./plugins/jsx";
+plugins.lightscript = lightscriptPlugin;
plugins.estree = estreePlugin;
plugins.flow = flowPlugin;
plugins.jsx = jsxPlugin;
-export function parse(input: string, options?: Options): File {
- return getParser(options, input).parse();
+export function parse(input, options) {
+ return new Parser(options, input).parse();
}
-export function parseExpression(input: string, options?: Options): Expression {
- const parser = getParser(options, input);
+export function parseExpression(input, options) {
+ const parser = new Parser(options, input);
if (parser.options.strictMode) {
parser.state.strict = true;
}
@@ -37,39 +34,3 @@ export function parseExpression(input: string, options?: Options): Expression {
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 } = {};
-
-/** Get a Parser class with plugins applied. */
-function getParserClass(pluginsFromOptions: $ReadOnlyArray): Class {
- // Filter out just the plugins that have an actual mixin associated with them.
- let pluginList = pluginsFromOptions.filter((p) => p === "estree" || p === "flow" || p === "jsx");
-
- if (pluginList.indexOf("flow") >= 0) {
- // ensure flow plugin loads last
- pluginList = pluginList.filter((plugin) => plugin !== "flow");
- pluginList.push("flow");
- }
-
- 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;
-}
diff --git a/src/options.js b/src/options.js
index 72d65ed0df..72c65f34a6 100755
--- a/src/options.js
+++ b/src/options.js
@@ -1,21 +1,16 @@
-// @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;
- strictMode: ?boolean;
- ranges: boolean;
-};
-
-export const defaultOptions: Options = {
+export const defaultOptions: {
+ sourceType: string,
+ sourceFilename: any,
+ startLine: number,
+ allowReturnOutsideFunction: boolean,
+ allowImportExportEverywhere: boolean,
+ allowSuperOutsideMethod: boolean,
+ plugins: Array,
+ strictMode: any
+} = {
// Source type ("script" or "module") for different semantics
sourceType: "script",
// Source filename.
@@ -35,21 +30,12 @@ export const defaultOptions: Options = {
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,
};
// Interpret and default an options object
-export function getOptions(opts: ?Options): Options {
- const options: any = {};
+export function getOptions(opts?: Object): Object {
+ const options = {};
for (const key in defaultOptions) {
options[key] = opts && key in opts ? opts[key] : defaultOptions[key];
}
diff --git a/src/parser/base.js b/src/parser/base.js
deleted file mode 100644
index 3795b71668..0000000000
--- a/src/parser/base.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// @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];
- }
-}
diff --git a/src/parser/comments.js b/src/parser/comments.js
index c39f91e44a..c032e85e07 100644
--- a/src/parser/comments.js
+++ b/src/parser/comments.js
@@ -1,7 +1,5 @@
/* eslint max-len: 0 */
-// @flow
-
/**
* Based on the comment attachment algorithm used in espree and estraverse.
*
@@ -26,132 +24,133 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import BaseParser from "./base";
-import type { Comment, Node } from "../types";
+import Parser from "./index";
function last(stack) {
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);
- }
+const pp = Parser.prototype;
- processComment(node: Node): void {
- if (node.type === "Program" && node.body.length > 0) return;
+pp.addComment = function (comment) {
+ if (this.filename) comment.loc.filename = this.filename;
+ this.state.trailingComments.push(comment);
+ this.state.leadingComments.push(comment);
+};
- const stack = this.state.commentStack;
+pp.processComment = function (node) {
+ if (node.type === "Program" && node.body.length > 0) return;
- let lastChild, trailingComments, i, j;
+ const stack = this.state.commentStack;
- 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;
- }
+ let 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 {
- const lastInStack = last(stack);
- if (stack.length > 0 && lastInStack.trailingComments && lastInStack.trailingComments[0].start >= node.end) {
- trailingComments = lastInStack.trailingComments;
- lastInStack.trailingComments = null;
- }
+ // 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;
}
-
- // Eating the stack.
- while (stack.length > 0 && last(stack).start >= node.start) {
- lastChild = stack.pop();
+ } else {
+ const lastInStack = last(stack);
+ if (stack.length > 0 && lastInStack.trailingComments && lastInStack.trailingComments[0].start >= node.end) {
+ trailingComments = lastInStack.trailingComments;
+ lastInStack.trailingComments = null;
}
+ }
- if (lastChild) {
- if (lastChild.leadingComments) {
- if (lastChild !== node && 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;
- }
+ // Eating the stack.
+ while (stack.length > 0 && last(stack).start >= node.start) {
+ lastChild = stack.pop();
+ }
+
+ if (lastChild) {
+ if (lastChild.leadingComments) {
+ if (lastChild !== node && 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;
+ }
+ } 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--;
}
}
-
- // 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;
+ }
+ 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;
}
}
- }
- this.state.commentPreviousNode = node;
+ // 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.
+ node.leadingComments = this.state.leadingComments.slice(0, i);
+ if ((node.leadingComments: Array).length === 0) {
+ node.leadingComments = null;
+ }
- if (trailingComments) {
- if (trailingComments.length && trailingComments[0].start >= node.start && last(trailingComments).end <= node.end) {
- node.innerComments = trailingComments;
- } else {
- node.trailingComments = trailingComments;
+ // 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;
}
}
+ }
- stack.push(node);
+ 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);
+};
diff --git a/src/parser/expression.js b/src/parser/expression.js
index 168e595415..1c075315f2 100644
--- a/src/parser/expression.js
+++ b/src/parser/expression.js
@@ -19,1104 +19,1368 @@
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
import { types as tt } from "../tokenizer/types";
-import LValParser from "./lval";
+import Parser from "./index";
import { reservedWords } from "../util/identifier";
-export default class ExpressionParser extends LValParser {
- // Check if property name clashes with already added.
- // Object/class getters and setters are not allowed to clash —
- // either with each other or with an init property — and in
- // strict mode, init properties are also not allowed to be repeated.
+const pp = Parser.prototype;
- checkPropClash(prop, propHash) {
- if (prop.computed || prop.kind) return;
+// Check if property name clashes with already added.
+// Object/class getters and setters are not allowed to clash —
+// either with each other or with an init property — and in
+// strict mode, init properties are also not allowed to be repeated.
- const key = prop.key;
- // It is either an Identifier or a String/NumericLiteral
- const name = key.type === "Identifier" ? key.name : String(key.value);
+pp.checkPropClash = function (prop, propHash) {
+ if (prop.computed || prop.kind) return;
- if (name === "__proto__") {
- if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
- propHash.proto = true;
+ 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;
+ }
+};
+
+// Convenience method to parse an Expression only
+pp.getExpression = function() {
+ this.nextToken();
+ const expr = this.parseExpression();
+ if (!this.match(tt.eof)) {
+ this.unexpected();
+ }
+ return expr;
+};
+
+// ### Expression parsing
+
+// These nest, from the most general expression type at the top to
+// 'atomic', nondivisible expression types at the bottom. Most of
+// the functions will simply let the function (s) below them parse,
+// and, *if* the syntactic construct they handle is present, wrap
+// the AST node that the inner parser gave them in another node.
+
+// Parse a full expression. The optional arguments are used to
+// forbid the `in` operator (in for loops initialization expressions)
+// and provide reference for storing '=' operator inside shorthand
+// property assignment in contexts where both object expression
+// and object pattern might appear (so it's possible to raise
+// delayed syntax error at correct position).
+
+pp.parseExpression = function (noIn, refShorthandDefaultPos) {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ const expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);
+ if (this.match(tt.comma)) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.expressions = [expr];
+ while (this.eat(tt.comma)) {
+ node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
}
+ this.toReferencedList(node.expressions);
+ return this.finishNode(node, "SequenceExpression");
}
+ return expr;
+};
- // Convenience method to parse an Expression only
- getExpression() {
- this.nextToken();
- const expr = this.parseExpression();
- if (!this.match(tt.eof)) {
- this.unexpected();
- }
- return expr;
+// Parse an assignment expression. This includes applications of
+// operators like `+=`.
+
+pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos) {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+
+ if (this.match(tt._yield) && this.state.inGenerator) {
+ let left = this.parseYield();
+ if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
+ return left;
}
- // ### Expression parsing
-
- // These nest, from the most general expression type at the top to
- // 'atomic', nondivisible expression types at the bottom. Most of
- // the functions will simply let the function (s) below them parse,
- // and, *if* the syntactic construct they handle is present, wrap
- // the AST node that the inner parser gave them in another node.
-
- // Parse a full expression. The optional arguments are used to
- // forbid the `in` operator (in for loops initialization expressions)
- // and provide reference for storing '=' operator inside shorthand
- // property assignment in contexts where both object expression
- // and object pattern might appear (so it's possible to raise
- // delayed syntax error at correct position).
-
- parseExpression(noIn, refShorthandDefaultPos) {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- const expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);
- if (this.match(tt.comma)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.expressions = [expr];
- while (this.eat(tt.comma)) {
- node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
- }
- this.toReferencedList(node.expressions);
- return this.finishNode(node, "SequenceExpression");
- }
- return expr;
+ let failOnShorthandAssign;
+ if (refShorthandDefaultPos) {
+ failOnShorthandAssign = false;
+ } else {
+ refShorthandDefaultPos = { start: 0 };
+ failOnShorthandAssign = true;
}
- // Parse an assignment expression. This includes applications of
- // operators like `+=`.
+ if (this.match(tt.parenL) || this.match(tt.name)) {
+ this.state.potentialArrowAt = this.state.start;
+ }
- parseMaybeAssign(noIn, refShorthandDefaultPos, afterLeftParse, refNeedsArrowPos) {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
+ let leftStartsLine, isNowAssign;
+ if (this.hasPlugin("lightscript")) {
+ leftStartsLine = this.seemsLikeStatementStart();
+ isNowAssign = this.eat(tt._now);
+ }
- if (this.match(tt._yield) && this.state.inGenerator) {
- let left = this.parseYield();
- if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
- return left;
+ let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos);
+ if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
+
+ // `varName: type = val`, with unwinding
+ // (this is a likely source of ambiguities, bugs, etc, since whitespace-block colons and others may be interpreted as type decaration)
+ let typeAnnotation;
+ if (this.hasPlugin("lightscript") && this.hasPlugin("flow") && this.match(tt.colon) && !this.isFollowedByLineBreak() && leftStartsLine) {
+ const state = this.state.clone();
+ try {
+ typeAnnotation = this.flowParseTypeAnnotation();
+ } catch (err) {
+ this.state = state;
}
- let failOnShorthandAssign;
- if (refShorthandDefaultPos) {
- failOnShorthandAssign = false;
- } else {
- refShorthandDefaultPos = { start: 0 };
- failOnShorthandAssign = true;
+ // if it's not followed by `=` after all, unwind
+ if (!(this.match(tt.eq) || this.match(tt.awaitArrow))) {
+ this.state = state;
}
+ }
- if (this.match(tt.parenL) || this.match(tt.name)) {
- this.state.potentialArrowAt = this.state.start;
- }
+ if (this.state.type.isAssign) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.operator = this.state.value;
- let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos);
- if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
- if (this.state.type.isAssign) {
- const node = this.startNodeAt(startPos, startLoc);
- node.operator = this.state.value;
- node.left = this.match(tt.eq) ? this.toAssignable(left, undefined, "assignment expression") : left;
- refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly
+ // `or` -> `||` etc.
+ if (this.hasPlugin("lightscript")) this.rewriteOperator(node);
- this.checkLVal(left, undefined, undefined, "assignment expression");
+ const isAwaitArrow = this.hasPlugin("lightscript") && this.match(tt.awaitArrow);
+ if (isAwaitArrow && this.isLineBreak()) return left;
- if (left.extra && left.extra.parenthesized) {
- let errorMsg;
- if (left.type === "ObjectPattern") {
- errorMsg = "`({a}) = 0` use `({a} = 0)`";
- } else if (left.type === "ArrayPattern") {
- errorMsg = "`([a]) = 0` use `([a] = 0)`";
- }
- if (errorMsg) {
- this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`);
- }
+ node.left = this.match(tt.eq) || isAwaitArrow ? this.toAssignable(left, undefined, "assignment expression") : left;
+ refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly
+
+ this.checkLVal(left, undefined, undefined, "assignment expression");
+
+ if (left.extra && left.extra.parenthesized) {
+ let errorMsg;
+ if (left.type === "ObjectPattern") {
+ errorMsg = "`({a}) = 0` use `({a} = 0)`";
+ } else if (left.type === "ArrayPattern") {
+ errorMsg = "`([a]) = 0` use `([a] = 0)`";
}
+ if (errorMsg) {
+ this.raise(left.start, `You're trying to assign to a parenthesized expression, eg. instead of ${errorMsg}`);
+ }
+ }
+ if (typeAnnotation) {
+ node.left.typeAnnotation = typeAnnotation;
+ node.left.end = typeAnnotation.end;
+ node.left.loc.end = typeAnnotation.loc.end;
+ }
+
+ if (isAwaitArrow) {
+ node.right = this.parseAwaitArrow(node.left);
+ } else {
this.next();
node.right = this.parseMaybeAssign(noIn);
- return this.finishNode(node, "AssignmentExpression");
- } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
- this.unexpected(refShorthandDefaultPos.start);
}
- return left;
+ if (this.hasPlugin("lightscript")) {
+ node.isNowAssign = isNowAssign;
+ }
+
+ return this.finishNode(node, "AssignmentExpression");
+ } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
+ this.unexpected(refShorthandDefaultPos.start);
}
- // Parse a ternary conditional (`?:`) operator.
+ if (this.hasPlugin("lightscript") && isNowAssign) {
+ if (left.type === "UpdateExpression" || noIn) {
+ left.isNowAssign = true;
+ } else {
+ this.unexpected(left.start, "`now` can only be used with assignments and updates.");
+ }
+ }
+
+ return left;
+};
+
+// Parse a ternary conditional (`?:`) operator.
- parseMaybeConditional(noIn, refShorthandDefaultPos, refNeedsArrowPos) {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- const expr = this.parseExprOps(noIn, refShorthandDefaultPos);
- if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos, refNeedsArrowPos) {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ const expr = this.parseExprOps(noIn, refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
- return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos);
+ return this.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos);
+};
+
+pp.parseConditional = function (expr, noIn, startPos, startLoc) {
+ if (this.eat(tt.question)) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.test = expr;
+ node.consequent = this.parseMaybeAssign();
+ this.expect(tt.colon);
+ node.alternate = this.parseMaybeAssign(noIn);
+ return this.finishNode(node, "ConditionalExpression");
}
+ return expr;
+};
- parseConditional(expr, noIn, startPos, startLoc) {
- if (this.eat(tt.question)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.test = expr;
- node.consequent = this.parseMaybeAssign();
- this.expect(tt.colon);
- node.alternate = this.parseMaybeAssign(noIn);
- return this.finishNode(node, "ConditionalExpression");
- }
+// Start the precedence parser.
+
+pp.parseExprOps = function (noIn, refShorthandDefaultPos) {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ const expr = this.parseMaybeUnary(refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
return expr;
+ } else {
+ return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
}
+};
- // Start the precedence parser.
+// Parse binary operators with the operator precedence parsing
+// algorithm. `left` is the left-hand side of the operator.
+// `minPrec` provides context that allows the function to stop and
+// defer further parser to one of its callers when it encounters an
+// operator that has a lower precedence than the set it is parsing.
- parseExprOps(noIn, refShorthandDefaultPos) {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- const expr = this.parseMaybeUnary(refShorthandDefaultPos);
- if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
- return expr;
- } else {
- return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
+pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
+ // correct ASI failures.
+ if (this.hasPlugin("lightscript") && this.isLineBreak()) {
+
+ // if it's a newline followed by a unary +/-, bail so it can be parsed separately.
+ if (this.match(tt.plusMin) && !this.isNextCharWhitespace()) {
+ return left;
}
}
- // Parse binary operators with the operator precedence parsing
- // algorithm. `left` is the left-hand side of the operator.
- // `minPrec` provides context that allows the function to stop and
- // defer further parser to one of its callers when it encounters an
- // operator that has a lower precedence than the set it is parsing.
-
- parseExprOp(left, leftStartPos, leftStartLoc, minPrec, noIn) {
- const prec = this.state.type.binop;
- if (prec != null && (!noIn || !this.match(tt._in))) {
- if (prec > minPrec) {
- const node = this.startNodeAt(leftStartPos, leftStartLoc);
- node.left = left;
- node.operator = this.state.value;
-
- if (
- node.operator === "**" &&
- left.type === "UnaryExpression" &&
- left.extra &&
- !left.extra.parenthesizedArgument &&
- !left.extra.parenthesized
- ) {
- this.raise(left.argument.start, "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.");
- }
-
- const op = this.state.type;
- this.next();
+ if (this.hasPlugin("lightscript") && this.isBitwiseOp()) {
+ this.unexpected(null, "Bitwise operators have been disabled in LightScript. " +
+ "Use the stdlib functions instead (eg; `bitwiseAnd(a, b)`).");
+ }
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, op.rightAssociative ? prec - 1 : prec, noIn);
+ const prec = this.state.type.binop;
+ if (prec != null && (!noIn || !this.match(tt._in))) {
+ if (prec > minPrec) {
+ const node = this.startNodeAt(leftStartPos, leftStartLoc);
+ node.left = left;
+ node.operator = this.state.value;
- this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression");
- return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
+ // `or` -> `||` etc.
+ if (this.hasPlugin("lightscript")) this.rewriteOperator(node);
+
+ if (
+ node.operator === "**" &&
+ left.type === "UnaryExpression" &&
+ left.extra &&
+ !left.extra.parenthesizedArgument &&
+ !left.extra.parenthesized
+ ) {
+ this.raise(left.argument.start, "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.");
}
+
+ const op = this.state.type;
+ this.next();
+
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, op.rightAssociative ? prec - 1 : prec, noIn);
+
+ this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression");
+ return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
}
- return left;
}
+ return left;
+};
- // Parse unary operators, both prefix and postfix.
+// Parse unary operators, both prefix and postfix.
- parseMaybeUnary(refShorthandDefaultPos) {
- if (this.state.type.prefix) {
- const node = this.startNode();
- const update = this.match(tt.incDec);
- node.operator = this.state.value;
- node.prefix = true;
- this.next();
+pp.parseMaybeUnary = function (refShorthandDefaultPos) {
+ if (this.state.type.prefix) {
+ if (this.hasPlugin("lightscript") && this.match(tt.plusMin)) {
+ if (this.isNextCharWhitespace()) this.unexpected(null, "Unary +/- cannot be followed by a space in lightscript.");
+ }
- const argType = this.state.type;
- node.argument = this.parseMaybeUnary();
+ const node = this.startNode();
+ const update = this.match(tt.incDec);
+ node.operator = this.state.value;
- this.addExtra(node, "parenthesizedArgument", argType === tt.parenL && (!node.argument.extra || !node.argument.extra.parenthesized));
+ // `not` -> `!` etc.
+ if (this.hasPlugin("lightscript")) this.rewriteOperator(node);
- if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
- this.unexpected(refShorthandDefaultPos.start);
- }
+ node.prefix = true;
+ this.next();
- if (update) {
- this.checkLVal(node.argument, undefined, undefined, "prefix operation");
- } else if (this.state.strict && node.operator === "delete" && node.argument.type === "Identifier") {
- this.raise(node.start, "Deleting local variable in strict mode");
- }
+ const argType = this.state.type;
+ node.argument = this.parseMaybeUnary();
+
+ this.addExtra(node, "parenthesizedArgument", argType === tt.parenL && (!node.argument.extra || !node.argument.extra.parenthesized));
- return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
+ this.unexpected(refShorthandDefaultPos.start);
}
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- let expr = this.parseExprSubscripts(refShorthandDefaultPos);
- if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
- while (this.state.type.postfix && !this.canInsertSemicolon()) {
- const node = this.startNodeAt(startPos, startLoc);
- node.operator = this.state.value;
- node.prefix = false;
- node.argument = expr;
- this.checkLVal(expr, undefined, undefined, "postfix operation");
- this.next();
- expr = this.finishNode(node, "UpdateExpression");
+ if (update) {
+ this.checkLVal(node.argument, undefined, undefined, "prefix operation");
+ } else if (this.state.strict && node.operator === "delete" && node.argument.type === "Identifier") {
+ this.raise(node.start, "Deleting local variable in strict mode");
}
- return expr;
+
+ return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
}
- // Parse call, dot, and `[]`-subscript expressions.
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ let expr = this.parseExprSubscripts(refShorthandDefaultPos);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+ while (this.state.type.postfix && !this.canInsertSemicolon()) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.operator = this.state.value;
+ node.prefix = false;
+ node.argument = expr;
+ this.checkLVal(expr, undefined, undefined, "postfix operation");
+ this.next();
+ expr = this.finishNode(node, "UpdateExpression");
+ }
+ return expr;
+};
- parseExprSubscripts(refShorthandDefaultPos) {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- const potentialArrowAt = this.state.potentialArrowAt;
- const expr = this.parseExprAtom(refShorthandDefaultPos);
+// Parse call, dot, and `[]`-subscript expressions.
- if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) {
- return expr;
- }
+pp.parseExprSubscripts = function (refShorthandDefaultPos) {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ const potentialArrowAt = this.state.potentialArrowAt;
+ const expr = this.parseExprAtom(refShorthandDefaultPos);
- if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
- return expr;
- }
+ if (expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt) {
+ return expr;
+ }
- return this.parseSubscripts(expr, startPos, startLoc);
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
+ return expr;
}
- parseSubscripts(base, startPos, startLoc, noCalls) {
- for (;;) {
- if (!noCalls && this.eat(tt.doubleColon)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.object = base;
- node.callee = this.parseNoCallExpr();
- return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls);
- } else if (this.eat(tt.dot)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.object = base;
+ return this.parseSubscripts(expr, startPos, startLoc);
+};
+
+pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
+ for (;;) {
+ if (!noCalls && this.eat(tt.doubleColon)) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.object = base;
+ node.callee = this.parseNoCallExpr();
+ return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls);
+ } else if (this.match(tt.dot)) {
+ // require indentation
+ if (this.hasPlugin("lightscript") && this.isNonIndentedBreakFrom(startPos)) {
+ if (this.lookahead().type === tt.num) {
+ this.unexpected(null, "Either indent for array access or use a leading zero for a decimal.");
+ }
+ this.unexpected(null, "Indentation required.");
+ }
+
+ // catch malformed decimals (but allow `0.0.toString()`, which is actually valid)
+ if (this.hasPlugin("lightscript") && base.type === "NumericLiteral") {
+ if (!(base.extra && base.extra.raw && base.extra.raw.match(/\./))) {
+ this.unexpected(null, "Numbers with a decimal must end in a number (eg; `1.0`) in LightScript.");
+ }
+ }
+
+ this.next();
+ const node = this.startNodeAt(startPos, startLoc);
+ node.object = base;
+
+ // parse arr.0 as arr[0]
+ if (this.hasPlugin("lightscript") && this.match(tt.num)) {
+ node.property = this.parseExprAtom();
+ node.computed = true;
+ } else {
node.property = this.parseIdentifier(true);
node.computed = false;
- base = this.finishNode(node, "MemberExpression");
- } else if (this.eat(tt.bracketL)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.object = base;
+ }
+ base = this.finishNode(node, "MemberExpression");
+ } else if (this.hasPlugin("lightscript") && this.match(tt.elvis)) {
+ if (this.isNonIndentedBreakFrom(startPos)) {
+ this.unexpected(null, "Indentation required.");
+ }
+ // `x?.y`
+ const node = this.startNodeAt(startPos, startLoc);
+ const op = this.state.value;
+ this.next();
+ node.object = base;
+ if (op === "?.") {
+ // arr?.0 -> arr?[0]
+ if (this.match(tt.num)) {
+ node.property = this.parseLiteral(this.state.value, "NumericLiteral");
+ node.computed = true;
+ } else {
+ node.property = this.parseIdentifier(true);
+ node.computed = false;
+ }
+ } else if (op === "?[") {
node.property = this.parseExpression();
node.computed = true;
this.expect(tt.bracketR);
- base = this.finishNode(node, "MemberExpression");
- } else if (!noCalls && this.match(tt.parenL)) {
- const possibleAsync = this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon();
- this.next();
-
- const node = this.startNodeAt(startPos, startLoc);
- node.callee = base;
- node.arguments = this.parseCallExpressionArguments(tt.parenR, possibleAsync);
- if (node.callee.type === "Import" && node.arguments.length !== 1) {
- this.raise(node.start, "import() requires exactly one argument");
- }
- base = this.finishNode(node, "CallExpression");
+ } else {
+ this.unexpected();
+ }
+ base = this.finishNode(node, "SafeMemberExpression");
+ } else if (this.hasPlugin("lightscript") && !noCalls && this.match(tt.tilde)) {
+ if (this.isNonIndentedBreakFrom(startPos)) {
+ this.unexpected(null, "Indentation required.");
+ }
+ this.next();
+ const node = this.startNodeAt(startPos, startLoc);
+ node.left = base;
+ // allow `this`, Identifier or MemberExpression, but not calls
+ const right = this.match(tt._this) ? this.parseExprAtom() : this.parseIdentifier();
+ node.right = this.parseSubscripts(right, this.state.start, this.state.startLoc, true);
+
+ this.expect(tt.parenL);
+ node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
+ base = this.finishNode(node, "TildeCallExpression");
+ } else if (!(this.hasPlugin("lightscript") && this.isNonIndentedBreakFrom(startPos)) && this.eat(tt.bracketL)) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.object = base;
+ node.property = this.parseExpression();
+ node.computed = true;
+ this.expect(tt.bracketR);
+ base = this.finishNode(node, "MemberExpression");
+ } else if (!noCalls && this.match(tt.parenL) && !(this.hasPlugin("lightscript") && this.isLineBreak())) {
+ const possibleAsync = this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon();
+ const possibleArrow = possibleAsync || this.hasPlugin("lightscript");
+ const refShorthandDefaultPos = possibleArrow ? { start: 0 } : undefined;
+ this.next();
- if (possibleAsync && this.shouldParseAsyncArrow()) {
- return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node);
+ const node = this.startNodeAt(startPos, startLoc);
+ node.callee = base;
+ node.arguments = this.parseCallExpressionArguments(tt.parenR, possibleArrow, refShorthandDefaultPos);
+ if (node.callee.type === "Import" && node.arguments.length !== 1) {
+ this.raise(node.start, "import() requires exactly one argument");
+ }
+ base = this.finishNode(node, "CallExpression");
+
+ if (possibleAsync && this.shouldParseAsyncArrow()) {
+ return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node);
+ } else if (this.hasPlugin("lightscript") && this.shouldParseAsyncArrow()) {
+
+ // could be a function call followed by a colon-block, eg `if fn(): blah`
+ // or an annotated NamedArrowExpression, eg `fn(): blah ->`
+ // Disambiguating is hard, especially with good error messages...
+ // TODO: relentlessly test edge cases.
+ // This is one of the most likely sources of bugs I've encountered yet.
+ if (this.match(tt.colon)) {
+ const state = this.state.clone();
+ try {
+ return this.parseNamedArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node);
+ } catch (err) {
+ if (this.match(tt.arrow)) throw err;
+ this.state = state;
+ }
} else {
- this.toReferencedList(node.arguments);
+ return this.parseNamedArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node);
}
- } else if (this.match(tt.backQuote)) {
- const node = this.startNodeAt(startPos, startLoc);
- node.tag = base;
- node.quasi = this.parseTemplate(true);
- base = this.finishNode(node, "TaggedTemplateExpression");
- } else {
+ }
+ if (refShorthandDefaultPos && refShorthandDefaultPos.start) {
+ this.unexpected(refShorthandDefaultPos.start);
+ }
+ this.toReferencedList(node.arguments);
+ } else if (this.hasPlugin("lightscript") && this.hasPlugin("flow") && this.isRelational("<")) {
+ // `fn() ->`, c/p of the above, but for `<`
+ const node = this.startNodeAt(startPos, startLoc), state = this.state.clone();
+ node.callee = base;
+ try {
+ node.typeParameters = this.flowParseTypeParameterDeclaration();
+ this.expect(tt.parenL);
+ } catch (err) {
+ this.state = state;
return base;
}
+ node.arguments = this.parseCallExpressionArguments(tt.parenR, true, { start: 0 });
+ if (!this.shouldParseArrow()) this.unexpected();
+ return this.parseNamedArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node);
+ } else if (this.match(tt.backQuote) && !(this.hasPlugin("lightscript") && this.isLineBreak())) {
+ const node = this.startNodeAt(startPos, startLoc);
+ node.tag = base;
+ node.quasi = this.parseTemplate();
+ base = this.finishNode(node, "TaggedTemplateExpression");
+ } else {
+ return base;
}
}
-
- parseCallExpressionArguments(close, possibleAsyncArrow) {
- const elts = [];
- let innerParenStart;
- let first = true;
-
- while (!this.eat(close)) {
- if (first) {
- first = false;
+};
+
+// last parameter (refShorthandDefaultPos) added for lightscript,
+// but should be contributed upstream because it fixes a minor bug in babylon:
+// `async({ foo = 1 })` parses as a function call but should raise an error.
+pp.parseCallExpressionArguments = function (close, possibleAsyncArrow, refShorthandDefaultPos) {
+ const elts = [];
+ let innerParenStart;
+ let first = true;
+
+ while (!this.eat(close)) {
+ if (first) {
+ first = false;
+ } else {
+ if (this.hasPlugin("lightscript")) {
+ this.expectCommaOrLineBreak();
} else {
this.expect(tt.comma);
- if (this.eat(close)) break;
- }
-
- // we need to make sure that if this is an async arrow functions, that we don't allow inner parens inside the params
- if (this.match(tt.parenL) && !innerParenStart) {
- innerParenStart = this.state.start;
}
-
- elts.push(this.parseExprListItem(false, possibleAsyncArrow ? { start: 0 } : undefined, possibleAsyncArrow ? { start: 0 } : undefined));
+ if (this.eat(close)) break;
}
- // we found an async arrow function so let's not allow any inner parens
- if (possibleAsyncArrow && innerParenStart && this.shouldParseAsyncArrow()) {
- this.unexpected();
+ // we need to make sure that if this is an async arrow functions, that we don't allow inner parens inside the params
+ if (this.match(tt.parenL) && !innerParenStart) {
+ innerParenStart = this.state.start;
}
- return elts;
+ elts.push(this.parseExprListItem(false, refShorthandDefaultPos, refShorthandDefaultPos));
}
- shouldParseAsyncArrow() {
- return this.match(tt.arrow);
+ // we found an async arrow function so let's not allow any inner parens
+ if (possibleAsyncArrow && innerParenStart && this.shouldParseAsyncArrow()) {
+ this.unexpected();
}
- parseAsyncArrowFromCallExpression(node, call) {
- this.expect(tt.arrow);
- return this.parseArrowExpression(node, call.arguments, true);
+ return elts;
+};
+
+pp.shouldParseAsyncArrow = function () {
+ if (this.hasPlugin("lightscript") && this.isLineBreak()) {
+ return false;
}
+ return this.match(tt.arrow);
+};
+
+pp.parseAsyncArrowFromCallExpression = function (node, call) {
+ this.check(tt.arrow);
+ return this.parseArrowExpression(node, call.arguments, true);
+};
+
+// Parse a no-call expression (like argument of `new` or `::` operators).
+
+pp.parseNoCallExpr = function () {
+ const startPos = this.state.start;
+ const startLoc = this.state.startLoc;
+ return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
+};
+
+// Parse an atomic expression — either a single token that is an
+// expression, an expression started by a keyword like `function` or
+// `new`, or an expression wrapped in punctuation like `()`, `[]`,
+// or `{}`.
+
+pp.parseExprAtom = function (refShorthandDefaultPos) {
+ const canBeArrow = this.state.potentialArrowAt === this.state.start;
+ let node;
+
+ switch (this.state.type) {
+ case tt._super:
+ if (!this.state.inMethod && !this.options.allowSuperOutsideMethod) {
+ this.raise(this.state.start, "'super' outside of function or class");
+ }
+
+ node = this.startNode();
+ this.next();
+ if (!this.match(tt.parenL) && !this.match(tt.bracketL) && !this.match(tt.dot)) {
+ this.unexpected();
+ }
+ if (this.match(tt.parenL) && this.state.inMethod !== "constructor" && !this.options.allowSuperOutsideMethod) {
+ this.raise(node.start, "super() outside of class constructor");
+ }
+ return this.finishNode(node, "Super");
- // Parse a no-call expression (like argument of `new` or `::` operators).
+ case tt._import:
+ if (!this.hasPlugin("dynamicImport")) this.unexpected();
- parseNoCallExpr() {
- const startPos = this.state.start;
- const startLoc = this.state.startLoc;
- return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
- }
+ node = this.startNode();
+ this.next();
+ if (!this.match(tt.parenL)) {
+ this.unexpected(null, tt.parenL);
+ }
+ return this.finishNode(node, "Import");
- // Parse an atomic expression — either a single token that is an
- // expression, an expression started by a keyword like `function` or
- // `new`, or an expression wrapped in punctuation like `()`, `[]`,
- // or `{}`.
+ case tt._this:
+ node = this.startNode();
+ this.next();
+ return this.finishNode(node, "ThisExpression");
- parseExprAtom(refShorthandDefaultPos) {
- const canBeArrow = this.state.potentialArrowAt === this.state.start;
- let node;
+ case tt._yield:
+ if (this.state.inGenerator) this.unexpected();
- switch (this.state.type) {
- case tt._super:
- if (!this.state.inMethod && !this.options.allowSuperOutsideMethod) {
- this.raise(this.state.start, "'super' outside of function or class");
- }
+ case tt.name:
+ node = this.startNode();
+ const allowAwait = this.state.value === "await" && this.state.inAsync;
+ const allowYield = this.shouldAllowYieldIdentifier();
+ const id = this.parseIdentifier(allowAwait || allowYield);
- node = this.startNode();
- this.next();
- if (!this.match(tt.parenL) && !this.match(tt.bracketL) && !this.match(tt.dot)) {
- this.unexpected();
- }
- if (this.match(tt.parenL) && this.state.inMethod !== "constructor" && !this.options.allowSuperOutsideMethod) {
- this.raise(node.start, "super() is only valid inside a class constructor. Make sure the method name is spelled exactly as 'constructor'.");
+ if (id.name === "await") {
+ if (this.state.inAsync || this.inModule) {
+ return this.parseAwait(node);
}
- return this.finishNode(node, "Super");
+ } else if (id.name === "async" && this.match(tt._function) && !this.canInsertSemicolon()) {
+ this.next();
+ return this.parseFunction(node, false, false, true);
+ } else if (canBeArrow && id.name === "async" && this.match(tt.name)) {
+ const params = [this.parseIdentifier()];
+ this.check(tt.arrow);
+ // let foo = bar => {};
+ return this.parseArrowExpression(node, params, true);
+ }
- case tt._import:
- if (!this.hasPlugin("dynamicImport")) this.unexpected();
+ if (canBeArrow && !this.canInsertSemicolon() && this.match(tt.arrow)) {
+ return this.parseArrowExpression(node, [id]);
+ }
- node = this.startNode();
- this.next();
- if (!this.match(tt.parenL)) {
- this.unexpected(null, tt.parenL);
- }
- return this.finishNode(node, "Import");
+ return id;
- case tt._this:
- node = this.startNode();
+ case tt._do:
+ if (this.hasPlugin("doExpressions")) {
+ const node = this.startNode();
this.next();
- return this.finishNode(node, "ThisExpression");
+ const oldInFunction = this.state.inFunction;
+ const oldLabels = this.state.labels;
+ this.state.labels = [];
+ this.state.inFunction = false;
+ node.body = this.parseBlock(false, true);
+ this.state.inFunction = oldInFunction;
+ this.state.labels = oldLabels;
+ return this.finishNode(node, "DoExpression");
+ }
- case tt._yield:
- if (this.state.inGenerator) this.unexpected();
+ case tt.regexp:
+ const value = this.state.value;
+ node = this.parseLiteral(value.value, "RegExpLiteral");
+ node.pattern = value.pattern;
+ node.flags = value.flags;
+ return node;
- case tt.name:
- node = this.startNode();
- const allowAwait = this.state.value === "await" && this.state.inAsync;
- const allowYield = this.shouldAllowYieldIdentifier();
- const id = this.parseIdentifier(allowAwait || allowYield);
+ case tt.num:
+ return this.parseLiteral(this.state.value, "NumericLiteral");
- if (id.name === "await") {
- if (this.state.inAsync || this.inModule) {
- return this.parseAwait(node);
- }
- } else if (id.name === "async" && this.match(tt._function) && !this.canInsertSemicolon()) {
- this.next();
- return this.parseFunction(node, false, false, true);
- } else if (canBeArrow && id.name === "async" && this.match(tt.name)) {
- const params = [this.parseIdentifier()];
- this.expect(tt.arrow);
- // let foo = bar => {};
- return this.parseArrowExpression(node, params, true);
- }
+ case tt.string:
+ return this.parseLiteral(this.state.value, "StringLiteral");
- if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
- return this.parseArrowExpression(node, [id]);
- }
+ case tt._null:
+ node = this.startNode();
+ this.next();
+ return this.finishNode(node, "NullLiteral");
- return id;
-
- case tt._do:
- if (this.hasPlugin("doExpressions")) {
- const node = this.startNode();
- this.next();
- const oldInFunction = this.state.inFunction;
- const oldLabels = this.state.labels;
- this.state.labels = [];
- this.state.inFunction = false;
- node.body = this.parseBlock(false, true);
- this.state.inFunction = oldInFunction;
- this.state.labels = oldLabels;
- return this.finishNode(node, "DoExpression");
- }
+ case tt._true: case tt._false:
+ node = this.startNode();
+ node.value = this.match(tt._true);
+ this.next();
+ return this.finishNode(node, "BooleanLiteral");
- case tt.regexp:
- const value = this.state.value;
- node = this.parseLiteral(value.value, "RegExpLiteral");
- node.pattern = value.pattern;
- node.flags = value.flags;
- return node;
+ case tt.parenL:
+ return this.parseParenAndDistinguishExpression(null, null, canBeArrow);
- case tt.num:
- return this.parseLiteral(this.state.value, "NumericLiteral");
+ case tt.bracketL:
+ node = this.startNode();
+ this.next();
+ if (this.hasPlugin("lightscript") && this.match(tt._for)) {
+ return this.parseArrayComprehension(node);
+ }
+ node.elements = this.parseExprList(tt.bracketR, true, refShorthandDefaultPos);
+ this.toReferencedList(node.elements);
+ return this.finishNode(node, "ArrayExpression");
- case tt.string:
- return this.parseLiteral(this.state.value, "StringLiteral");
+ case tt.braceL:
+ return this.parseObj(false, refShorthandDefaultPos);
- case tt._null:
- node = this.startNode();
- this.next();
- return this.finishNode(node, "NullLiteral");
+ case tt._function:
+ return this.parseFunctionExpression();
- case tt._true: case tt._false:
- node = this.startNode();
- node.value = this.match(tt._true);
- this.next();
- return this.finishNode(node, "BooleanLiteral");
+ case tt.at:
+ this.parseDecorators();
- case tt.parenL:
- return this.parseParenAndDistinguishExpression(null, null, canBeArrow);
+ case tt._class:
+ node = this.startNode();
+ this.takeDecorators(node);
+ return this.parseClass(node, false);
- case tt.bracketL:
- node = this.startNode();
- this.next();
- node.elements = this.parseExprList(tt.bracketR, true, refShorthandDefaultPos);
- this.toReferencedList(node.elements);
- return this.finishNode(node, "ArrayExpression");
-
- case tt.braceL:
- return this.parseObj(false, refShorthandDefaultPos);
+ case tt._new:
+ return this.parseNew();
- case tt._function:
- return this.parseFunctionExpression();
+ case tt.backQuote:
+ return this.parseTemplate();
- case tt.at:
- this.parseDecorators();
+ case tt.doubleColon:
+ node = this.startNode();
+ this.next();
+ node.object = null;
+ const callee = node.callee = this.parseNoCallExpr();
+ if (callee.type === "MemberExpression") {
+ return this.finishNode(node, "BindExpression");
+ } else {
+ this.raise(callee.start, "Binding should be performed on object property.");
+ }
- case tt._class:
+ case tt._if:
+ if (this.hasPlugin("lightscript")) {
node = this.startNode();
- this.takeDecorators(node);
- return this.parseClass(node, false);
-
- case tt._new:
- return this.parseNew();
+ return this.parseIfExpression(node);
+ }
- case tt.backQuote:
- return this.parseTemplate(false);
+ case tt.arrow:
+ if (this.hasPlugin("lightscript")) {
+ node = this.startNode();
+ return this.parseArrowExpression(node, []);
+ }
- case tt.doubleColon:
+ case tt.awaitArrow:
+ if (this.hasPlugin("lightscript")) {
node = this.startNode();
+ const isSafe = this.state.value === " 1) {
- val = this.startNodeAt(innerStartPos, innerStartLoc);
- val.expressions = exprList;
- this.toReferencedList(val.expressions);
- this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
+ if (this.match(tt.ellipsis)) {
+ const spreadNodeStartPos = this.state.start;
+ const spreadNodeStartLoc = this.state.startLoc;
+ spreadStart = this.state.start;
+ exprList.push(this.parseParenItem(this.parseRest(), spreadNodeStartPos, spreadNodeStartLoc));
+ break;
} else {
- val = exprList[0];
+ exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem, refNeedsArrowPos));
}
+ }
+ const innerEndPos = this.state.start;
+ const innerEndLoc = this.state.startLoc;
+ this.expect(tt.parenR);
- this.addExtra(val, "parenthesized", true);
- this.addExtra(val, "parenStart", startPos);
+ let arrowNode = this.startNodeAt(startPos, startLoc);
+ if (canBeArrow && this.shouldParseArrow() && (arrowNode = this.parseArrow(arrowNode))) {
+ for (const param of exprList) {
+ if (param.extra && param.extra.parenthesized) this.unexpected(param.extra.parenStart);
+ }
- return val;
+ return this.parseArrowExpression(arrowNode, exprList);
}
- shouldParseArrow() {
- return !this.canInsertSemicolon();
+ if (!exprList.length) {
+ this.unexpected(this.state.lastTokStart);
}
-
- parseArrow(node) {
- if (this.eat(tt.arrow)) {
- return node;
- }
+ if (optionalCommaStart) this.unexpected(optionalCommaStart);
+ if (spreadStart) this.unexpected(spreadStart);
+ if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
+ if (refNeedsArrowPos.start) this.unexpected(refNeedsArrowPos.start);
+
+ if (exprList.length > 1) {
+ val = this.startNodeAt(innerStartPos, innerStartLoc);
+ val.expressions = exprList;
+ this.toReferencedList(val.expressions);
+ this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
+ } else {
+ val = exprList[0];
}
- parseParenItem(node) {
- return node;
- }
- // New's precedence is slightly tricky. It must allow its argument
- // to be a `[]` or dot subscript expression, but not a call — at
- // least, not without wrapping it in parentheses. Thus, it uses the
+ this.addExtra(val, "parenthesized", true);
+ this.addExtra(val, "parenStart", startPos);
- parseNew() {
- const node = this.startNode();
- const meta = this.parseIdentifier(true);
+ return val;
+};
- if (this.eat(tt.dot)) {
- const metaProp = this.parseMetaProperty(node, meta, "target");
+pp.shouldParseArrow = function () {
+ return !this.canInsertSemicolon();
+};
- if (!this.state.inFunction) {
- this.raise(metaProp.property.start, "new.target can only be used in functions");
- }
+pp.parseArrow = function (node) {
+ if (this.match(tt.arrow)) {
+ return node;
+ }
+};
- return metaProp;
- }
+pp.parseParenItem = function (node) {
+ return node;
+};
- node.callee = this.parseNoCallExpr();
+// New's precedence is slightly tricky. It must allow its argument
+// to be a `[]` or dot subscript expression, but not a call — at
+// least, not without wrapping it in parentheses. Thus, it uses the
- if (this.eat(tt.parenL)) {
- node.arguments = this.parseExprList(tt.parenR);
- this.toReferencedList(node.arguments);
- } else {
- node.arguments = [];
- }
+pp.parseNew = function () {
+ const node = this.startNode();
+ const meta = this.parseIdentifier(true);
- return this.finishNode(node, "NewExpression");
+ if (this.eat(tt.dot)) {
+ return this.parseMetaProperty(node, meta, "target");
}
- // Parse template expression.
+ node.callee = this.parseNoCallExpr();
- parseTemplateElement(isTagged) {
- const elem = this.startNode();
- if (this.state.value === null) {
- if (!isTagged) {
- this.raise(this.state.invalidTemplateEscapePosition, "Invalid escape sequence in template");
- } else {
- this.state.invalidTemplateEscapePosition = null;
- }
- }
- elem.value = {
- raw: this.input.slice(this.state.start, this.state.end).replace(/\r\n?/g, "\n"),
- cooked: this.state.value
- };
- this.next();
- elem.tail = this.match(tt.backQuote);
- return this.finishNode(elem, "TemplateElement");
+ // Lightscript ASI: don't parse as arg list if on a different line
+ if (!(this.hasPlugin("lightscript") && this.isLineBreak()) && this.eat(tt.parenL)) {
+ node.arguments = this.parseExprList(tt.parenR);
+ this.toReferencedList(node.arguments);
+ } else {
+ node.arguments = [];
}
- parseTemplate(isTagged) {
- const node = this.startNode();
- this.next();
- node.expressions = [];
- let curElt = this.parseTemplateElement(isTagged);
- node.quasis = [curElt];
- while (!curElt.tail) {
- this.expect(tt.dollarBraceL);
- node.expressions.push(this.parseExpression());
- this.expect(tt.braceR);
- node.quasis.push(curElt = this.parseTemplateElement(isTagged));
+ return this.finishNode(node, "NewExpression");
+};
+
+// Parse template expression.
+
+pp.parseTemplateElement = function () {
+ const elem = this.startNode();
+ elem.value = {
+ raw: this.input.slice(this.state.start, this.state.end).replace(/\r\n?/g, "\n"),
+ cooked: this.state.value
+ };
+ this.next();
+ elem.tail = this.match(tt.backQuote);
+ return this.finishNode(elem, "TemplateElement");
+};
+
+pp.parseTemplate = function () {
+ const node = this.startNode();
+ this.next();
+ node.expressions = [];
+ let curElt = this.parseTemplateElement();
+ node.quasis = [curElt];
+ while (!curElt.tail) {
+ this.expect(tt.dollarBraceL);
+ node.expressions.push(this.parseExpression());
+ this.expect(tt.braceR);
+ node.quasis.push(curElt = this.parseTemplateElement());
+ }
+ this.next();
+ return this.finishNode(node, "TemplateLiteral");
+};
+
+// Parse an object literal or binding pattern.
+
+pp.parseObj = function (isPattern, refShorthandDefaultPos) {
+ let decorators = [];
+ const propHash = Object.create(null);
+ let first = true;
+ const node = this.startNode();
+
+ node.properties = [];
+ this.next();
+
+ // `for` keyword begins an object comprehension.
+ if (this.hasPlugin("lightscript") && this.match(tt._for)) {
+ // ...however, `{ for: x }` is a legal JS object.
+ if (this.lookahead().type !== tt.colon) {
+ return this.parseObjectComprehension(node);
}
- this.next();
- return this.finishNode(node, "TemplateLiteral");
}
- // Parse an object literal or binding pattern.
-
- parseObj(isPattern, refShorthandDefaultPos) {
- let decorators = [];
- const propHash = Object.create(null);
- let first = true;
- const node = this.startNode();
-
- node.properties = [];
- this.next();
+ let firstRestLocation = null;
- let firstRestLocation = null;
-
- while (!this.eat(tt.braceR)) {
- if (first) {
- first = false;
+ while (!this.eat(tt.braceR)) {
+ if (first) {
+ first = false;
+ } else {
+ if (this.hasPlugin("lightscript")) {
+ this.expectCommaOrLineBreak();
} else {
this.expect(tt.comma);
- if (this.eat(tt.braceR)) break;
}
+ if (this.eat(tt.braceR)) break;
+ }
- while (this.match(tt.at)) {
- decorators.push(this.parseDecorator());
- }
+ while (this.match(tt.at)) {
+ decorators.push(this.parseDecorator());
+ }
- let prop = this.startNode(), isGenerator = false, isAsync = false, startPos, startLoc;
- if (decorators.length) {
- prop.decorators = decorators;
- decorators = [];
- }
+ let prop = this.startNode(), isGenerator = false, isAsync = false, startPos, startLoc;
+ if (decorators.length) {
+ prop.decorators = decorators;
+ decorators = [];
+ }
- if (this.hasPlugin("objectRestSpread") && this.match(tt.ellipsis)) {
- prop = this.parseSpread(isPattern ? { start: 0 } : undefined);
- prop.type = isPattern ? "RestElement" : "SpreadElement";
- if (isPattern) this.toAssignable(prop.argument, true, "object pattern");
- node.properties.push(prop);
- if (isPattern) {
- const position = this.state.start;
- if (firstRestLocation !== null) {
- this.unexpected(firstRestLocation, "Cannot have multiple rest elements when destructuring");
- } else if (this.eat(tt.braceR)) {
- break;
- } else if (this.match(tt.comma) && this.lookahead().type === tt.braceR) {
- this.unexpected(position, "A trailing comma is not permitted after the rest element");
- } else {
- firstRestLocation = position;
- continue;
- }
+ if (this.hasPlugin("objectRestSpread") && this.match(tt.ellipsis)) {
+ prop = this.parseSpread(isPattern ? { start: 0 } : undefined);
+ prop.type = isPattern ? "RestProperty" : "SpreadProperty";
+ if (isPattern) this.toAssignable(prop.argument, true, "object pattern");
+ node.properties.push(prop);
+ if (isPattern) {
+ const position = this.state.start;
+ if (firstRestLocation !== null) {
+ this.unexpected(firstRestLocation, "Cannot have multiple rest elements when destructuring");
+ } else if (this.eat(tt.braceR)) {
+ break;
+ } else if (this.match(tt.comma) && this.lookahead().type === tt.braceR) {
+ // TODO: temporary rollback
+ // this.unexpected(position, "A trailing comma is not permitted after the rest element");
+ continue;
} else {
+ firstRestLocation = position;
continue;
}
+ } else {
+ continue;
}
+ }
- prop.method = false;
- prop.shorthand = false;
+ prop.method = false;
+ prop.shorthand = false;
- if (isPattern || refShorthandDefaultPos) {
- startPos = this.state.start;
- startLoc = this.state.startLoc;
- }
+ if (isPattern || refShorthandDefaultPos) {
+ startPos = this.state.start;
+ startLoc = this.state.startLoc;
+ }
- if (!isPattern) {
- isGenerator = this.eat(tt.star);
- }
+ if (!isPattern) {
+ isGenerator = this.eat(tt.star);
+ }
- if (!isPattern && this.isContextual("async")) {
- if (isGenerator) this.unexpected();
+ if (!isPattern && this.isContextual("async")) {
+ if (isGenerator) this.unexpected();
- const asyncId = this.parseIdentifier();
- if (this.match(tt.colon) || this.match(tt.parenL) || this.match(tt.braceR) || this.match(tt.eq) || this.match(tt.comma)) {
- prop.key = asyncId;
- prop.computed = false;
- } else {
- isAsync = true;
- if (this.hasPlugin("asyncGenerators")) isGenerator = this.eat(tt.star);
- this.parsePropertyName(prop);
- }
+ const asyncId = this.parseIdentifier();
+ if (this.match(tt.colon) || this.match(tt.parenL) || this.match(tt.braceR) || this.match(tt.eq) || this.match(tt.comma) || (this.hasPlugin("lightscript") && this.isLineBreak())) {
+ prop.key = asyncId;
+ prop.computed = false;
} else {
+ isAsync = true;
+ if (this.hasPlugin("asyncGenerators")) isGenerator = this.eat(tt.star);
this.parsePropertyName(prop);
}
-
- this.parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos);
- this.checkPropClash(prop, propHash);
-
- if (prop.shorthand) {
- this.addExtra(prop, "shorthand", true);
- }
-
- node.properties.push(prop);
+ } else {
+ this.parsePropertyName(prop);
}
- if (firstRestLocation !== null) {
- this.unexpected(firstRestLocation, "The rest element has to be the last element when destructuring");
- }
+ this.parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos);
+ this.checkPropClash(prop, propHash);
- if (decorators.length) {
- this.raise(this.state.start, "You have trailing decorators with no property");
+ if (prop.shorthand) {
+ this.addExtra(prop, "shorthand", true);
}
- return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
+ node.properties.push(prop);
}
- isGetterOrSetterMethod(prop, isPattern) {
- return !isPattern &&
- !prop.computed &&
- prop.key.type === "Identifier" &&
- (prop.key.name === "get" || prop.key.name === "set") &&
- (
- this.match(tt.string) || // get "string"() {}
- this.match(tt.num) || // get 1() {}
- this.match(tt.bracketL) || // get ["string"]() {}
- this.match(tt.name) || // get foo() {}
- this.state.type.keyword // get debugger() {}
- );
+ if (firstRestLocation !== null) {
+ this.unexpected(firstRestLocation, "The rest element has to be the last element when destructuring");
}
- // get methods aren't allowed to have any parameters
- // set methods must have exactly 1 parameter
- checkGetterSetterParamCount(method) {
- const paramCount = method.kind === "get" ? 0 : 1;
- if (method.params.length !== paramCount) {
- const start = method.start;
- if (method.kind === "get") {
- this.raise(start, "getter should have no params");
- } else {
- this.raise(start, "setter should have exactly one param");
- }
- }
+ if (decorators.length) {
+ this.raise(this.state.start, "You have trailing decorators with no property");
}
- parseObjectMethod(prop, isGenerator, isAsync, isPattern) {
- if (isAsync || isGenerator || this.match(tt.parenL)) {
- if (isPattern) this.unexpected();
- prop.kind = "method";
- prop.method = true;
- this.parseMethod(prop, isGenerator, isAsync);
-
- return this.finishNode(prop, "ObjectMethod");
- }
-
- if (this.isGetterOrSetterMethod(prop, isPattern)) {
- if (isGenerator || isAsync) this.unexpected();
- prop.kind = prop.key.name;
- this.parsePropertyName(prop);
- this.parseMethod(prop);
- this.checkGetterSetterParamCount(prop);
-
- return this.finishNode(prop, "ObjectMethod");
+ return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
+};
+
+pp.isGetterOrSetterMethod = function (prop, isPattern) {
+ return !isPattern &&
+ !prop.computed &&
+ prop.key.type === "Identifier" &&
+ (prop.key.name === "get" || prop.key.name === "set") &&
+ !(this.hasPlugin("lightscript") && this.isLineBreak()) &&
+ (
+ this.match(tt.string) || // get "string"() {}
+ this.match(tt.num) || // get 1() {}
+ this.match(tt.bracketL) || // get ["string"]() {}
+ this.match(tt.name) || // get foo() {}
+ this.state.type.keyword // get debugger() {}
+ );
+};
+
+// get methods aren't allowed to have any parameters
+// set methods must have exactly 1 parameter
+pp.checkGetterSetterParamCount = function (method) {
+ const paramCount = method.kind === "get" ? 0 : 1;
+ if (method.params.length !== paramCount) {
+ const start = method.start;
+ if (method.kind === "get") {
+ this.raise(start, "getter should have no params");
+ } else {
+ this.raise(start, "setter should have exactly one param");
}
}
+};
- parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos) {
- if (this.eat(tt.colon)) {
- prop.value = isPattern ? this.parseMaybeDefault(this.state.start, this.state.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
-
- return this.finishNode(prop, "ObjectProperty");
- }
+pp.parseObjectMethod = function (prop, isGenerator, isAsync, isPattern) {
+ if (isAsync || isGenerator || this.match(tt.parenL)) {
+ if (isPattern) this.unexpected();
+ prop.kind = "method";
+ prop.method = true;
+ this.parseMethod(prop, isGenerator, isAsync);
- if (!prop.computed && prop.key.type === "Identifier") {
- this.checkReservedWord(prop.key.name, prop.key.start, true, true);
+ return this.finishNode(prop, "ObjectMethod");
+ }
- if (isPattern) {
- prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
- } else if (this.match(tt.eq) && refShorthandDefaultPos) {
- if (!refShorthandDefaultPos.start) {
- refShorthandDefaultPos.start = this.state.start;
- }
- prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
- } else {
- prop.value = prop.key.__clone();
- }
- prop.shorthand = true;
+ if (this.isGetterOrSetterMethod(prop, isPattern)) {
+ if (isGenerator || isAsync) this.unexpected();
+ prop.kind = prop.key.name;
+ this.parsePropertyName(prop);
+ this.parseMethod(prop);
+ this.checkGetterSetterParamCount(prop);
- return this.finishNode(prop, "ObjectProperty");
- }
+ return this.finishNode(prop, "ObjectMethod");
}
+};
- parseObjPropValue(prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
- const node =
- this.parseObjectMethod(prop, isGenerator, isAsync, isPattern) ||
- this.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos);
-
- if (!node) this.unexpected();
+pp.parseObjectProperty = function (prop, startPos, startLoc, isPattern, refShorthandDefaultPos) {
+ if (this.eat(tt.colon)) {
+ prop.value = isPattern ? this.parseMaybeDefault(this.state.start, this.state.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
- return node;
+ return this.finishNode(prop, "ObjectProperty");
}
- parsePropertyName(prop) {
- if (this.eat(tt.bracketL)) {
- prop.computed = true;
- prop.key = this.parseMaybeAssign();
- this.expect(tt.bracketR);
+ if (!prop.computed && prop.key.type === "Identifier") {
+ if (isPattern) {
+ this.checkReservedWord(prop.key.name, prop.key.start, true, true);
+ prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
+ } else if (this.match(tt.eq) && refShorthandDefaultPos) {
+ if (!refShorthandDefaultPos.start) {
+ refShorthandDefaultPos.start = this.state.start;
+ }
+ prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key.__clone());
} else {
- prop.computed = false;
- const oldInPropertyName = this.state.inPropertyName;
- this.state.inPropertyName = true;
- prop.key = (this.match(tt.num) || this.match(tt.string)) ? this.parseExprAtom() : this.parseIdentifier(true);
- this.state.inPropertyName = oldInPropertyName;
+ prop.value = prop.key.__clone();
}
- return prop.key;
- }
-
- // Initialize empty function node.
+ prop.shorthand = true;
- initFunction(node, isAsync) {
- node.id = null;
- node.generator = false;
- node.expression = false;
- node.async = !!isAsync;
+ return this.finishNode(prop, "ObjectProperty");
+ }
+};
+
+pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
+ const node =
+ this.parseObjectMethod(prop, isGenerator, isAsync, isPattern) ||
+ this.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos);
+
+ if (!node) this.unexpected();
+
+ return node;
+};
+
+pp.parsePropertyName = function (prop) {
+ if (this.eat(tt.bracketL)) {
+ prop.computed = true;
+ prop.key = this.parseMaybeAssign();
+ this.expect(tt.bracketR);
+ } else {
+ prop.computed = false;
+ const oldInPropertyName = this.state.inPropertyName;
+ this.state.inPropertyName = true;
+ prop.key = (this.match(tt.num) || this.match(tt.string)) ? this.parseExprAtom() : this.parseIdentifier(true);
+ this.state.inPropertyName = oldInPropertyName;
+ }
+ return prop.key;
+};
+
+// Initialize empty function node.
+
+pp.initFunction = function (node, isAsync) {
+ node.id = null;
+ node.generator = false;
+ node.expression = false;
+ node.async = !!isAsync;
+};
+
+// Parse object or class method.
+
+pp.parseMethod = function (node, isGenerator, isAsync) {
+ const oldInMethod = this.state.inMethod;
+ this.state.inMethod = node.kind || true;
+ this.initFunction(node, isAsync);
+ this.expect(tt.parenL);
+ node.params = this.parseBindingList(tt.parenR);
+ node.generator = !!isGenerator;
+
+ if (this.hasPlugin("lightscript") && this.match(tt.colon)) {
+ const oldNoAnonFunctionType = this.state.noAnonFunctionType;
+ this.state.noAnonFunctionType = true;
+ node.returnType = this.flowParseTypeAnnotation();
+ this.state.noAnonFunctionType = oldNoAnonFunctionType;
}
- // Parse object or class method.
+ if (this.hasPlugin("lightscript") && this.match(tt.arrow)) {
+ this.parseArrowType(node);
- parseMethod(node, isGenerator, isAsync) {
- const oldInMethod = this.state.inMethod;
- this.state.inMethod = node.kind || true;
- this.initFunction(node, isAsync);
- this.expect(tt.parenL);
- node.params = this.parseBindingList(tt.parenR);
- node.generator = !!isGenerator;
+ if (node.kind === "get" || node.kind === "set") {
+ this.checkGetterSetterParamCount(node);
+ }
+
+ this.parseArrowFunctionBody(node);
+ } else {
this.parseFunctionBody(node);
- this.state.inMethod = oldInMethod;
- return node;
}
- // Parse arrow function expression with given parameters.
+ this.state.inMethod = oldInMethod;
+ return node;
+};
+
+// Parse arrow function expression with given parameters.
+
+pp.parseArrowExpression = function (node, params, isAsync) {
+ this.initFunction(node, isAsync);
+ node.params = this.toAssignableList(params, true, "arrow function parameters");
- parseArrowExpression(node, params, isAsync) {
- this.initFunction(node, isAsync);
- node.params = this.toAssignableList(params, true, "arrow function parameters");
+ if (this.hasPlugin("lightscript")) {
+ this.check(tt.arrow);
+ this.parseArrowType(node);
+ this.parseArrowFunctionBody(node);
+ } else {
+ this.expect(tt.arrow);
this.parseFunctionBody(node, true);
- return this.finishNode(node, "ArrowFunctionExpression");
}
-
- isStrictBody(node, isExpression) {
- if (!isExpression && node.body.directives.length) {
- for (const directive of (node.body.directives: Array