├── .gitmodules ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── codeql.yml │ └── build-test.yml ├── __tests__ ├── theAssets │ ├── main.cpp │ ├── toolchain.cmake │ ├── CMakeLists.txt │ └── CMakePresets.json ├── unit.test.ts └── functional.test.ts ├── .eslintignore ├── dist ├── action.js ├── clang.json ├── gcc.json ├── msvc.json ├── cmake.json ├── module.js ├── all.json └── esprima.js ├── docs └── imgs │ └── annotation.png ├── tsconfig.json ├── .gitignore ├── src ├── action.ts └── cmake-action.ts ├── tsconfig-base.json ├── jest.config.js ├── .eslintrc.js ├── LICENSE.txt ├── package.json ├── CONTRIBUTING.md ├── action.yml └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [lukka] 2 | -------------------------------------------------------------------------------- /__tests__/theAssets/main.cpp: -------------------------------------------------------------------------------- 1 | int main() { return 0; } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | .eslintrc.js 4 | *.js -------------------------------------------------------------------------------- /__tests__/theAssets/toolchain.cmake: -------------------------------------------------------------------------------- 1 | message(status "toolchain.cmake is being used!") -------------------------------------------------------------------------------- /dist/action.js: -------------------------------------------------------------------------------- 1 | if (!yy.ast) { 2 | yy.ast = _ast; 3 | _ast.initialize(); 4 | } 5 | -------------------------------------------------------------------------------- /docs/imgs/annotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukka/run-cmake/HEAD/docs/imgs/annotation.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "build" 5 | } 6 | } -------------------------------------------------------------------------------- /__tests__/theAssets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(test_project) 3 | 4 | add_executable(main main.cpp) 5 | 6 | install(TARGETS main DESTINATION bin) 7 | 8 | include(CPack) -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/*.js 3 | *.map 4 | *.js.log 5 | token.txt 6 | *.vsix 7 | *.zip 8 | .taskkey 9 | .vscode 10 | *.tgz 11 | build 12 | __tests__/build Directory 13 | __tests__/b 14 | !dist 15 | .npmrc 16 | .DS_Store 17 | __tests__/theAssets/builds 18 | coverage -------------------------------------------------------------------------------- /src/action.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2020-2021-2022-2023 Luca Cappa 2 | // Released under the term specified in file LICENSE.txt 3 | // SPDX short identifier: MIT 4 | 5 | import * as cmakeaction from './cmake-action' 6 | 7 | // Main entry point of the task. 8 | cmakeaction.main().catch(error => console.error("main() failed!", error)); 9 | -------------------------------------------------------------------------------- /dist/clang.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "clang", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /dist/gcc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s*(?:(?:fatal)?\\s(warning|error)):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /dist/msvc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "msvc", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*)\\((\\d+)\\)\\s*:\\s*(?:fatal\\s*)?(error|warning)\\s+(?:.*)?([\\d]{4}):(\\s+.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "code": 4, 11 | "severity": 3, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /dist/cmake.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "cmake", 5 | "pattern": [ 6 | { 7 | "regexp": "^\\s*CMake (Error|Warning) at (.+):(\\d+)\\s*(.*)?:", 8 | "severity": 1, 9 | "file": 2, 10 | "line": 3 11 | }, 12 | { 13 | "regexp": "^\\s+(.*)$", 14 | "message": 1 15 | } 16 | ] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /tsconfig-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "sourceMap": true, 6 | "strict": true, 7 | // https://github.com/microsoft/TypeScript/issues/9858: 8 | // Use this folder as the relative basis for computing the paths in outDir". Which 9 | // files are part of your compilation is a separate question that is answered by 10 | // files and/or include/exclude 11 | //"rootDir": ".", 12 | "outDir": "build" 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "build" 17 | ] 18 | } -------------------------------------------------------------------------------- /dist/module.js: -------------------------------------------------------------------------------- 1 | var _ast = { 2 | 3 | initialize: function() { 4 | this._nodes = []; 5 | this._node = {}; 6 | this._stash = []; 7 | }, 8 | 9 | set: function(props) { 10 | for (var k in props) this._node[k] = props[k]; 11 | return this._node; 12 | }, 13 | 14 | node: function(obj) { 15 | if (arguments.length) this._node = obj; 16 | return this._node; 17 | }, 18 | 19 | push: function() { 20 | this._nodes.push(this._node); 21 | this._node = {}; 22 | }, 23 | 24 | unshift: function() { 25 | this._nodes.unshift(this._node); 26 | this._node = {}; 27 | }, 28 | 29 | yield: function() { 30 | var _nodes = this._nodes; 31 | this.initialize(); 32 | return _nodes; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | restoreMocks: true, 4 | moduleFileExtensions: ['js', 'ts'], 5 | testEnvironment: 'node', 6 | testMatch: ['**/*.test.ts'], 7 | testRunner: 'jest-circus/runner', 8 | transform: { 9 | '^.+\\.ts$': 'ts-jest' 10 | }, 11 | verbose: true, 12 | silent: false, 13 | globals: { 14 | 'ts-jest': { 15 | tsconfig: './tsconfig.json', 16 | }, 17 | }, 18 | collectCoverage: true, 19 | coveragePathIgnorePatterns: [ 20 | "/build/", "/node_modules/", "/__tests__", "__tests__", "/src/action.ts" 21 | ], 22 | collectCoverageFrom: [ 23 | "src/*.ts", 24 | "!**/node_modules/**", 25 | "!**/build/**", 26 | "!**/dist/**" 27 | ] 28 | 29 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2020-2023 Luca Cappa 2 | // Released under the term specified in file LICENSE.txt 3 | // SPDX short identifier: MIT 4 | 5 | module.exports = { 6 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 7 | extends: [ 8 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 9 | ], 10 | parserOptions: { 11 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 12 | project: './tsconfig.json', 13 | tsconfigRootDir: __dirname, 14 | }, 15 | rules: { 16 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 17 | // e.g. "@typescript-eslint/explicit-function-return-type": "off", 18 | "@typescript-eslint/no-floating-promises": ["error"], 19 | "@typescript-eslint/no-explicit-any": ["off"], 20 | "@typescript-eslint/no-unused-vars": ["off"] 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2019-2020-2021-2023 Luca Cappa 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. -------------------------------------------------------------------------------- /__tests__/unit.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2020-2021-2022-2023 Luca Cappa 2 | // Released under the term specified in file LICENSE.txt 3 | // SPDX short identifier: MIT 4 | 5 | import * as core from '@actions/core' 6 | import * as cmakeaction from '../src/cmake-action' 7 | import * as runcmakelib from '@lukka/run-cmake-lib' 8 | 9 | jest.setTimeout(15 * 1000); 10 | // Mocks entire action-lib module. 11 | jest.mock("@lukka/run-cmake-lib"); 12 | 13 | describe('run-cmake unit tests', () => { 14 | test('run without exception', async () => { 15 | await cmakeaction.main(); 16 | }); 17 | 18 | test('run with exception, it must fail', async () => { 19 | // Arrange 20 | const setFailedMock = jest.spyOn(core, "setFailed"); 21 | jest.spyOn(runcmakelib.CMakeRunner, "run").mockImplementation(() => { throw new Error(); }) 22 | // Act 23 | await cmakeaction.main(); 24 | 25 | // The failure sets an exit code different than 0, and this will fail `npm run test`. 26 | // On node20+ on Linux/Windows (but not on macOS) this leads to a failing exit 27 | // code: https://github.com/jestjs/jest/issues/14501 28 | process.exitCode = 0; 29 | 30 | // Assert 31 | expect(setFailedMock).toBeCalledTimes(1); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "main" ] 9 | schedule: 10 | - cron: '22 12 * * 4' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: 'ubuntu-latest' 16 | timeout-minutes: 360 17 | permissions: 18 | actions: read 19 | contents: read 20 | security-events: write 21 | packages: read 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | language: [ 'javascript' ] 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@v3 35 | with: 36 | languages: ${{ matrix.language }} 37 | 38 | - uses: actions/setup-node@v4 39 | with: 40 | node-version: '20.x' 41 | - name: Authenticate to GitHub Packages 42 | run: | 43 | echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" > ~/.npmrc 44 | echo "@${{ github.repository_owner }}:registry=https://npm.pkg.github.com/" >> ~/.npmrc 45 | - run: | 46 | npm install 47 | npm run build 48 | npm run pack 49 | 50 | - name: Perform CodeQL Analysis 51 | uses: github/codeql-action/analyze@v3 52 | with: 53 | category: "/language:${{matrix.language}}" 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "run-cmake-action", 3 | "version": "10.0.0", 4 | "description": "GitHub action for running CMake.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/lukka/run-cmake-action" 8 | }, 9 | "author": "Luca Cappa (https://github.com/lukka)", 10 | "license": "MIT", 11 | "scripts": { 12 | "clean": "rm -rf ./build", 13 | "deployAssets": "cp ./node_modules/@lukka/assets-lib/src/matchers/*.json ./dist/", 14 | "build": "npm run deployAssets && npx tsc", 15 | "lint": "npx eslint .", 16 | "test": "npx jest", 17 | "pack": "npx ncc build build/src/action.js -o dist", 18 | "repack": "npm run build && npm run deployAssets && npm run lint && npm run pack" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^20.8.9", 22 | "@typescript-eslint/eslint-plugin": "^6.9.0", 23 | "@typescript-eslint/parser": "^6.9.0", 24 | "@vercel/ncc": "^0.38.1", 25 | "acorn": "^6.4.1", 26 | "eslint": "^8.52.0", 27 | "jest": "^29.7.0", 28 | "jest-circus": "^29.7.0", 29 | "minimist": ">=1.2.6", 30 | "ts-jest": "^29.1.1", 31 | "ts-node": "~10.9.1", 32 | "typescript": "^5.2.2", 33 | "yargs-parser": ">=13.1.2" 34 | }, 35 | "dependencies": { 36 | "@actions/core": "1.10.1", 37 | "@actions/exec": "^1.1.1", 38 | "@actions/github": "6.0.0", 39 | "@actions/glob": "0.4.0", 40 | "@actions/io": "1.1.3", 41 | "@lukka/action-lib": "4.1.6", 42 | "@lukka/assets-lib": "4.1.6", 43 | "@lukka/base-lib": "4.1.6", 44 | "@lukka/base-util-lib": "4.1.6", 45 | "@lukka/run-cmake-lib": "4.1.6", 46 | "@types/follow-redirects": "1.14.3", 47 | "@types/jest": "29.5.6", 48 | "follow-redirects": "^1.15.6", 49 | "jest-cli": "29.7.0", 50 | "strip-json-comments": "^3.0.1" 51 | } 52 | } -------------------------------------------------------------------------------- /dist/all.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "clang", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | }, 16 | { 17 | "owner": "cmake", 18 | "pattern": [ 19 | { 20 | "regexp": "^\\s*CMake (Error|Warning) at (.+):(\\d+)\\s*(.*)?:", 21 | "severity": 1, 22 | "file": 2, 23 | "line": 3 24 | }, 25 | { 26 | "regexp": "^\\s+(.*)$", 27 | "message": 1 28 | } 29 | ] 30 | }, 31 | { 32 | "owner": "gcc", 33 | "pattern": [ 34 | { 35 | "regexp": "^(.*):(\\d+):(\\d+):\\s*(?:(?:fatal)?\\s(warning|error)):\\s+(.*)$", 36 | "file": 1, 37 | "line": 2, 38 | "column": 3, 39 | "severity": 4, 40 | "message": 5 41 | } 42 | ] 43 | }, 44 | { 45 | "owner": "msvc-line", 46 | "pattern": [ 47 | { 48 | "regexp": "^(.*)\\((\\d+)\\)\\s*:\\s*(?:fatal\\s*)?(error|warning)\\s+(?:.*)?([\\d]{4}):(\\s+.*)$", 49 | "file": 1, 50 | "line": 2, 51 | "code": 4, 52 | "severity": 3, 53 | "message": 5 54 | } 55 | ] 56 | }, 57 | { 58 | "owner": "msvc-linecol", 59 | "pattern": [ 60 | { 61 | "regexp": "^(?:\\sWarning:\\s|\\sError:\\s)?(.*)\\((\\d+)(?:,(\\d+))?\\)\\s*:\\s*(?:fatal\\s*)?(error|warning)\\s+(?:.*)?([\\d]{4}):(\\s+.*)$", 62 | "file": 1, 63 | "line": 2, 64 | "column": 3, 65 | "code": 5, 66 | "severity": 4, 67 | "message": 6 68 | } 69 | ] 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | - [Contributing](#contributing) 3 | - [Prerequisites](#prerequisites) 4 | - [Setup for consuming GitHub Registry public packages](#setup-for-consuming-github-registry-public-packages) 5 | - [Build and lint](#build-and-lint) 6 | - [Packaging](#packaging) 7 | - [Testing](#testing) 8 | 9 | The software is provided as is, there is no warranty of any kind. All users are encouraged to improve the [source code](https://github.com/lukka/run-cmake) with fixes and new features contributed by means of Pull Requests. 10 | 11 | ## Prerequisites 12 | 13 | Run 14 | 15 | ```bash 16 | npm install 17 | ``` 18 | 19 | to populate the dependencies in `./node_modules` directory. 20 | 21 | ### Setup for consuming GitHub Registry public packages 22 | 23 | `run-cmake` depends on public NPM packages published by [lukka/run-cmake-vcpkg-action-libs](https://github.com/lukka/run-cmake-vcpkg-action-libs) in the [GitHub Packages registry](https://docs.github.com/en/free-pro-team@latest/packages/using-github-packages-with-your-projects-ecosystem/configuring-npm-for-use-with-github-packages). 24 | Unexpectedly, a public package still requires authentication when downloading it, hence if you want to `npm install` those packages correctly, you need to obtain a token with `read:packages` scope. Then create in the root of the repository a `.npmrc` file with the following content: 25 | 26 | ``` 27 | //npm.pkg.github.com/:_authToken=YOURTOKEN 28 | @lukka:registry=https://npm.pkg.github.com/ 29 | ``` 30 | 31 | __Note__: **Never commit this `.npmrc` file!** 32 | 33 | ## Build and lint 34 | Build with `tsc` running: 35 | 36 | > npm run build 37 | 38 | Launch `lint` by: 39 | 40 | > npm run lint 41 | 42 | ## Packaging 43 | To build, lint validate and package the extension for release purpose, run: 44 | 45 | > npm run pack 46 | 47 | ## Testing 48 | 49 | To build, pack and test: 50 | 51 | > npm run test 52 | 53 | To run test directly: 54 | 55 | > npx jest 56 | 57 | Validation tests on various scenarios are run using the workflows of the [Samples](./README.md#samples). 58 | -------------------------------------------------------------------------------- /__tests__/theAssets/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "default", 11 | "displayName": "Ninja Configure Settings", 12 | "description": "Sets build and install directories", 13 | "binaryDir": "${sourceDir}/builds/${presetName}", 14 | "generator": "Ninja" 15 | }, 16 | { 17 | "name": "default-toolchain", 18 | "displayName": "Ninja Configure Settings with toolchain", 19 | "description": "Sets build and install directories", 20 | "binaryDir": "${sourceDir}/builds/${presetName}-toolchain", 21 | "generator": "Ninja", 22 | "toolchainFile": "$env{TOOLCHAINFILE}" 23 | }, 24 | { 25 | "name": "default-multi", 26 | "displayName": "Ninja Multi-Config Configure Settings", 27 | "description": "Sets build and install directories", 28 | "binaryDir": "${sourceDir}/builds/${presetName}", 29 | "generator": "Ninja Multi-Config" 30 | } 31 | ], 32 | "buildPresets": [ 33 | { 34 | "name": "default", 35 | "configurePreset": "default", 36 | "displayName": "Build", 37 | "description": "Build" 38 | }, 39 | { 40 | "name": "default-multi", 41 | "configurePreset": "default-multi", 42 | "displayName": "Build Multi", 43 | "description": "Build Multi Configurations" 44 | } 45 | ], 46 | "testPresets": [ 47 | { 48 | "name": "default", 49 | "configurePreset": "default" 50 | }, 51 | { 52 | "name": "default-multi", 53 | "configurePreset": "default-multi" 54 | } 55 | ], 56 | "packagePresets": [ 57 | { 58 | "packageName": "default-package-name", 59 | "packageVersion": "0.1", 60 | "name": "default", 61 | "configurePreset": "default", 62 | "generators": [ 63 | "TGZ", 64 | "ZIP" 65 | ] 66 | }, 67 | { 68 | "packageName": "default-multi-package-name", 69 | "packageVersion": "0.1", 70 | "name": "default-multi", 71 | "configurePreset": "default-multi", 72 | "generators": [ 73 | "TGZ", 74 | "ZIP" 75 | ], 76 | "configurations": [ 77 | "Debug" 78 | ] 79 | } 80 | ], 81 | "workflowPresets": [ 82 | { 83 | "name": "default-workflow", 84 | "steps": [ 85 | { 86 | "type": "configure", 87 | "name": "default-multi" 88 | }, 89 | { 90 | "type": "build", 91 | "name": "default-multi" 92 | } 93 | ] 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /src/cmake-action.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2020-2021-2022-2023 Luca Cappa 2 | // Released under the term specified in file LICENSE.txt 3 | // SPDX short identifier: MIT 4 | 5 | import * as libaction from '@lukka/action-lib'; 6 | import * as runcmakelib from '@lukka/run-cmake-lib' 7 | import * as cmakeglobals from '@lukka/run-cmake-lib/build/cmake-globals' 8 | import * as vcpkgglobals from '@lukka/run-cmake-lib/build/vcpkg-globals' 9 | import * as core from '@actions/core' 10 | 11 | export async function main(): Promise { 12 | const actionLib: libaction.ActionLib = new libaction.ActionLib(); 13 | try { 14 | const configurePreset = actionLib.getInput(cmakeglobals.configurePreset, false); 15 | const buildPreset = actionLib.getInput(cmakeglobals.buildPreset, false); 16 | const testPreset = actionLib.getInput(cmakeglobals.testPreset, false); 17 | const packagePreset = actionLib.getInput(cmakeglobals.packagePreset, false); 18 | const workflowPreset = actionLib.getInput(cmakeglobals.workflowPreset, false); 19 | const workflowPresetCmdStringFormat = actionLib.getInput(cmakeglobals.workflowPresetFormat, false); 20 | const configurePresetCmdStringFormat = actionLib.getInput(cmakeglobals.configurePresetFormat, false); 21 | const buildPresetCmdStringFormat = actionLib.getInput(cmakeglobals.buildPresetFormat, false); 22 | const testPresetCmdStringFormat = actionLib.getInput(cmakeglobals.testPresetFormat, false); 23 | const packagePresetCmdStringFormat = actionLib.getInput(cmakeglobals.packagePresetFormat, false); 24 | const configurePresetAdditionalArgs = actionLib.getInput(cmakeglobals.configurePresetAdditionalArgs, false); 25 | const buildPresetAdditionalArgs = actionLib.getInput(cmakeglobals.buildPresetAdditionalArgs, false); 26 | const testPresetAdditionalArgs = actionLib.getInput(cmakeglobals.testPresetAdditionalArgs, false); 27 | const packagePresetAdditionalArgs = actionLib.getInput(cmakeglobals.packagePresetAdditionalArgs, false); 28 | const runVcpkgEnvFormatString = actionLib.getInput(vcpkgglobals.runVcpkgEnvFormatStringInput, false); 29 | await runcmakelib.CMakeRunner.run( 30 | actionLib, 31 | workflowPreset, workflowPresetCmdStringFormat, 32 | configurePreset, configurePresetCmdStringFormat, configurePresetAdditionalArgs, 33 | buildPreset, buildPresetCmdStringFormat, buildPresetAdditionalArgs, 34 | testPreset, testPresetCmdStringFormat, testPresetAdditionalArgs, 35 | packagePreset, packagePresetCmdStringFormat, packagePresetAdditionalArgs, 36 | runVcpkgEnvFormatString); 37 | actionLib.info('run-cmake action execution succeeded'); 38 | process.exitCode = 0; 39 | } catch (err) { 40 | const error: Error = err as Error; 41 | if (error?.stack) { 42 | actionLib.info(error.stack); 43 | } 44 | const errorAsString = (err ?? "undefined error").toString(); 45 | core.setFailed(`run-cmake action execution failed: '${errorAsString}'`); 46 | process.exitCode = -1000; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: 'build-test' 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 1 * * FRI' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build_and_test: 12 | name: '${{ matrix.os }}: build and test' 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | os: [ubuntu-latest, macos-latest, windows-latest] 18 | 19 | env: 20 | VCPKG_ROOT: 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - uses: actions/setup-node@v4 26 | with: 27 | node-version: '20.x' 28 | # How to consume GitHub packages, from this message (!): https://github.community/t/download-from-github-package-registry-without-authentication/14407/35 29 | # Inspired from here: https://github.com/jcansdale-test/npmrc-configurations/blob/master/.github/workflows/blank.yml 30 | - name: Authenticate to GitHub Packages 31 | run: | 32 | echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" > ~/.npmrc 33 | echo "@${{ github.repository_owner }}:registry=https://npm.pkg.github.com/" >> ~/.npmrc 34 | - uses: lukka/get-cmake@latest 35 | - run: npm install 36 | - run: npm run build 37 | - run: npm run lint 38 | - run: npm run pack 39 | - run: npm run test 40 | - name: Coveralls 41 | uses: coverallsapp/github-action@master 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | parallel: true 45 | flag-name: ${{ matrix.os }}-build_and_unit_test 46 | base-path: ${{ runner.os != 'macOS' && '__tests__/theAssets/vcpkg_project/' || '' }} 47 | debug: true 48 | 49 | tests: 50 | name: '${{ matrix.os }}: functional tests' 51 | runs-on: ${{ matrix.os }} 52 | strategy: 53 | fail-fast: false 54 | matrix: 55 | os: [ubuntu-latest, macos-latest, windows-latest] 56 | 57 | env: 58 | VCPKG_ROOT: 59 | steps: 60 | - name: checkout this action 61 | uses: actions/checkout@v4 62 | with: 63 | submodules: true 64 | 65 | - uses: lukka/get-cmake@latest 66 | 67 | - name: test for run-cmake (Ninja) 68 | uses: ./ 69 | with: 70 | cmakeListsTxtPath: '${{ github.workspace }}/__tests__/theAssets/CMakeLists.txt' 71 | configurePreset: default 72 | buildPreset: default 73 | testPreset: default 74 | packagePreset: default 75 | 76 | - name: test for run-cmake (Ninja Multi-Config) 77 | uses: ./ 78 | with: 79 | cmakeListsTxtPath: '${{ github.workspace }}/__tests__/theAssets/CMakeLists.txt' 80 | configurePreset: default-multi 81 | buildPreset: default-multi 82 | testPreset: default-multi 83 | packagePreset: default-multi 84 | 85 | - name: build and test (skip configure) for run-cmake (Ninja Multi-Config) with specific config 86 | uses: ./ 87 | with: 88 | cmakeListsTxtPath: '${{ github.workspace }}/__tests__/theAssets/CMakeLists.txt' 89 | buildPreset: default-multi 90 | buildPresetAdditionalArgs: "['--config Release']" 91 | testPreset: default-multi 92 | testPresetAdditionalArgs: "['--build-config', 'Release']" 93 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020-2021-2023 Luca Cappa 2 | # Released under the term specified in file LICENSE.txt 3 | # SPDX short identifier: MIT 4 | 5 | name: 'run-cmake' 6 | description: 'Run CMake with CMakePreset.json to configure, build, package and test C/C++ source code.' 7 | author: 'Luca Cappa https://github.com/lukka' 8 | inputs: 9 | 10 | # The following 'inputs' are commonly defined by the user in the workflow. 11 | cmakeListsTxtPath: 12 | default: "${{ github.workspace }}/CMakeLists.txt" 13 | required: false 14 | description: "Path to CMakeLists.txt." 15 | workflowPreset: 16 | default: "" 17 | required: false 18 | description: "The name of the workflow preset. Optional, it cannot be used with any other preset input. This value is stored in the WORKFLOW_PRESET_NAME environment variable, and used by the default value of 'workflowPresetCmdString' input." 19 | configurePreset: 20 | default: "" 21 | required: false 22 | description: "The name of the configure preset. Optional, but at least one of the preset input must be provided. This value is stored in the CONFIGURE_PRESET_NAME environment variable, and used by the default value of 'configurePresetCmdString' input." 23 | buildPreset: 24 | default: "" 25 | required: false 26 | description: "The name of the build preset. Optional, but at least one of the preset input must be provided. This value is stored in the BUILD_PRESET_NAME environment variable, and used by the default value of 'buildPresetCmdString' input.'" 27 | testPreset: 28 | default: "" 29 | required: false 30 | description: "The name of the test preset (ctest). Optional, but at least one of the preset input must be provided. This value is stored in the TEST_PRESET_NAME environment variable, and used by the default value of 'testPresetCmdString' input.'" 31 | packagePreset: 32 | default: "" 33 | required: false 34 | description: "The name of the package preset (cpack). Optional, but at least one of the preset input must be provided. This value is stored in the PACKAGE_PRESET_NAME environment variable, and used by the default value of 'packagePresetCmdString' input.'" 35 | configurePresetAdditionalArgs: 36 | default: "[]" 37 | required: false 38 | description: "A string representing list of additional arguments for configuring. Optional. Useful when specifing additional variables such as, e.g., ['-DVARIABLE=NAME', '-DANOTHERVARIABLE=ANOTHERNAME']" 39 | buildPresetAdditionalArgs: 40 | default: "[]" 41 | required: false 42 | description: "A string representing list of additional arguments for building. Optional. Useful when specifing the config to build with a multi configuration generator, e.g., ['--config DEBUG']" 43 | testPresetAdditionalArgs: 44 | default: "[]" 45 | required: false 46 | description: "A string representing list of additional arguments for testing. Optional. Useful when specifing the config to test with a multi configuration generator, e.g., ['--config DEBUG']" 47 | packagePresetAdditionalArgs: 48 | default: "[]" 49 | required: false 50 | description: "A string representing list of additional arguments for cpack. Optional." 51 | 52 | 53 | # The following inputs are rarely set by the user, since the default values suffice the most common scenarios. 54 | useShell: 55 | default: true 56 | required: false 57 | description: "Specify which shell to be used when launching commands. 'true' means the default shell is used. 'false' means no shell is used. It also can be an absolute with arguments of the shell to spawn commands with." 58 | logCollectionRegExps: 59 | default: "\\s*\"(.+CMakeOutput\\.log)\"\\.\\s*;\\s*\"(.+CMakeError\\.log)\"\\.\\s*;\\s*(.+out\\.log)\\s*;\\s+(.+err\\.log)\\s*;\\s*(.+vcpkg.+\\.log)\\s*" 60 | required: false 61 | description: "Specifies a semicolon separated list of regular expressions that are used to identify log file paths in the workflow output. A regular expression must have a single capturing group, that is a single pair of parenthesis such as 'See also (.+.log)'. When a match occurs, the content of the file is written into the workflow output for disclosing its content to the user. The default regular expressions are for CMake's and vcpkg's log files." 62 | workflowPresetCmdString: 63 | default: "[`--workflow`, `--preset`, `$[env.WORKFLOW_PRESET_NAME]`, `--fresh`]" 64 | required: false 65 | description: "The CMake command format string to run the workflow steps." 66 | configurePresetCmdString: 67 | default: "[`--preset`, `$[env.CONFIGURE_PRESET_NAME]`]" 68 | required: false 69 | description: "The CMake command format string to configure and generate project files." 70 | buildPresetCmdString: 71 | default: "[`--build`, `--preset`, `$[env.BUILD_PRESET_NAME]`]" 72 | required: false 73 | description: "The CMake command format string to run the build." 74 | testPresetCmdString: 75 | default: "[`--preset`, `$[env.TEST_PRESET_NAME]`]" 76 | required: false 77 | description: "The CTest command format string to run test." 78 | packagePresetCmdString: 79 | default: "[`--preset`, `$[env.PACKAGE_PRESET_NAME]`]" 80 | required: false 81 | description: "The CPack command format string to package the project." 82 | runVcpkgEnvFormatString: 83 | default: "[`env`, `--bin`, `--include`, `--tools`, `--python`, `--triplet`, `$[env.VCPKG_DEFAULT_TRIPLET]`, `set`]" 84 | required: false 85 | description: "Specify the command line to dump the environment variables with the 'vcpkg env' command. This command is only used when setting up the environment for MSVC on Windows." 86 | 87 | runs: 88 | using: 'node20' 89 | main: './dist/index.js' 90 | 91 | branding: 92 | icon: 'terminal' 93 | color: 'green' 94 | -------------------------------------------------------------------------------- /__tests__/functional.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2020-2021-2023 Luca Cappa 2 | // Released under the term specified in file LICENSE.txt 3 | // SPDX short identifier: MIT 4 | 5 | import * as process from 'process' 6 | import * as cp from 'child_process' 7 | import * as path from 'path' 8 | import * as io from '@actions/io' 9 | 10 | jest.setTimeout(15 * 1000); 11 | 12 | const tempDirectory = path.join(__dirname, "temp Directory"); 13 | // Note: 'theAssets' must match the directory __tests__/theAssets/ 14 | const assetDirectory = path.join(__dirname, 'theAssets'); 15 | // This buildDirectory must be the root of all build directories defined in CMakePreset.json 16 | const buildDirectory = path.join(assetDirectory, "builds"); 17 | const testScript = path.join(__dirname, '..', 'dist', 'index.js'); 18 | const toolchainFile = path.join(assetDirectory, 'toolchain.cmake'); 19 | 20 | jest.setTimeout(15 * 1000) 21 | 22 | function envTest(useShell: boolean, envVarValue?: string): void { 23 | const TOOLCHAINFILE = "TOOLCHAINFILE"; 24 | process.env[TOOLCHAINFILE] = envVarValue; 25 | process.env.INPUT_USESHELL = useShell ? 'true' : 'false'; 26 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 27 | process.env.INPUT_CONFIGUREPRESET = "default-toolchain"; 28 | const options: cp.ExecSyncOptions = { 29 | env: process.env, 30 | stdio: "inherit", 31 | }; 32 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 33 | } 34 | 35 | function noInputsTest(): void { 36 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 37 | const options: cp.ExecSyncOptions = { 38 | env: process.env, 39 | stdio: "inherit", 40 | }; 41 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 42 | } 43 | 44 | describe('run-cmake functional tests', () => { 45 | beforeEach(async () => { 46 | await io.rmRF(buildDirectory); 47 | await io.rmRF(tempDirectory); 48 | await io.mkdirP(buildDirectory); 49 | await io.mkdirP(tempDirectory); 50 | Object.keys(process.env) 51 | .filter((key) => key.match(/^INPUT_/)) 52 | .forEach((key) => { 53 | delete process.env[key]; 54 | }); 55 | process.env.GITHUB_WORKSPACE = assetDirectory; 56 | delete process.env.VCPKG_ROOT; 57 | }, 300000); 58 | 59 | afterAll(async () => { 60 | try { 61 | await io.rmRF(buildDirectory); 62 | await io.rmRF(tempDirectory); 63 | } catch { 64 | console.log('Failed to remove test directories'); 65 | } 66 | }, 100000); 67 | 68 | test('configure with Ninja Multi-Config', () => { 69 | process.env.INPUT_CONFIGUREPRESET = "default-multi" 70 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 71 | const options: cp.ExecSyncOptions = { 72 | env: process.env, 73 | stdio: "inherit" 74 | }; 75 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 76 | }); 77 | 78 | test('configure with Ninja', () => { 79 | process.env.INPUT_CONFIGUREPRESET = "default" 80 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 81 | const options: cp.ExecSyncOptions = { 82 | env: process.env, 83 | stdio: "inherit" 84 | }; 85 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 86 | }); 87 | 88 | test('configure and build and test with Ninja', () => { 89 | process.env.INPUT_CONFIGUREPRESET = "default" 90 | process.env.INPUT_BUILDPRESET = "default" 91 | process.env.INPUT_TESTPRESET = "default" 92 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 93 | const options: cp.ExecSyncOptions = { 94 | env: process.env, 95 | stdio: "inherit" 96 | }; 97 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 98 | }); 99 | 100 | test('configure build package and test with Ninja (Multi-Config)', () => { 101 | process.env.INPUT_CONFIGUREPRESET = "default-multi" 102 | process.env.INPUT_BUILDPRESET = "default-multi" 103 | process.env.INPUT_TESTPRESET = "default-multi" 104 | process.env.INPUT_PACKAGEPRESET = "default-multi" 105 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 106 | const options: cp.ExecSyncOptions = { 107 | env: process.env, 108 | stdio: "inherit" 109 | }; 110 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 111 | }); 112 | 113 | test('configure build package and test with Ninja (Multi-Config) and VCPKG_ROOT (set to non yet built vcpkg executable) must succeed.', () => { 114 | process.env.INPUT_CONFIGUREPRESET = "default-multi"; 115 | process.env.INPUT_BUILDPRESET = "default-multi"; 116 | process.env.INPUT_TESTPRESET = "default-multi"; 117 | process.env.INPUT_PACKAGEPRESET = "default-multi"; 118 | process.env.VCPKG_ROOT = "does-not-exist-path"; 119 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 120 | const options: cp.ExecSyncOptions = { 121 | env: process.env, 122 | stdio: "inherit" 123 | }; 124 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 125 | }); 126 | 127 | test('run workflow', () => { 128 | process.env.INPUT_WORKFLOWPRESET = "default-workflow"; 129 | process.env.INPUT_BUILDPRESET = "default-multi"; // Must be ignored 130 | process.env.INPUT_TESTPRESET = "default-multi"; // Must be ignored 131 | process.env.INPUT_CMAKELISTSTXTPATH = path.join(assetDirectory, 'CMakeLists.txt'); 132 | const options: cp.ExecSyncOptions = { 133 | env: process.env, 134 | stdio: "inherit" 135 | }; 136 | console.log(cp.execSync(`node ${testScript}`, options)?.toString()); 137 | }); 138 | 139 | test('basic test for environment variables in input, no shell, it must throw', () => { 140 | // Building will use an environment variable that will not be 141 | // resolved since not being run inside a shell, and it will throw. 142 | expect(() => envTest(false, undefined)).toThrow(); 143 | }); 144 | 145 | test('should throw when no inputs are provided', () => { 146 | // Not specifying any input should fail. 147 | expect(() => noInputsTest()).toThrow(); 148 | }); 149 | 150 | test('basic test for environment variables in input, with shell', () => { 151 | envTest(true, toolchainFile); 152 | }); 153 | 154 | }); 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Action Status](https://github.com/lukka/run-cmake/workflows/build-test/badge.svg)](https://github.com/lukka/run-cmake/actions) 2 | 3 | [![Coverage Status](https://coveralls.io/repos/github/lukka/run-cmake/badge.svg?branch=main)](https://coveralls.io/github/lukka/run-cmake?branch=main) 4 | 5 | - [Quickstart with a C++ project template](#quickstart-with-a-c-project-template) 6 | - [**run-cmake@v10** runs CMake with CMakePresets.json](#run-cmakev10-runs-cmake-with-cmakepresetsjson) 7 | - [Quickstart with instructions](#quickstart-with-instructions) 8 | - [Best practises](#best-practises) 9 | - [Usage of github.workspace in multi platform workflows](#usage-of-githubworkspace-in-multi-platform-workflows) 10 | - [Action reference: all input/output parameters](#action-reference-all-inputoutput-parameters) 11 | - [Flowchart](#flowchart) 12 | - [Samples](#samples) 13 | - [Who is using `run-cmake`](#who-is-using-run-cmake) 14 | - [License](#license) 15 | - [Disclaimer](#disclaimer) 16 | - [Contributing](#contributing) 17 | 18 | # Quickstart with a C++ project template 19 | 20 | Take a look at this [C++ project template](https://github.com/lukka/CppCMakeVcpkgTemplate/tree/v11) that applies all the following instructions, but also shows how to create a __pure__ workflow without using special GitHub action that you cannot run locally on your development machine, but directly using the tools (`CMake`, `Ninja`, `vcpkg`, `C++` compilers) you already use daily. 21 | 22 | # [**run-cmake@v10** runs CMake with CMakePresets.json](https://github.com/marketplace/actions/run-cmake) 23 | 24 | The **run-cmake** action runs [CMake](https://cmake.org) on GitHub workflows leveraging [CMakePresets.json](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html). Note that usage of CMakePresets.json is required. 25 | 26 | Good companions are the [run-vcpkg](https://github.com/marketplace/actions/run-vcpkg) action and the [get-cmake](https://github.com/marketplace/actions/get-cmake) action. 27 | 28 | Special features which provide added value over a pure workflow are: 29 | - annotations for `CMake` errors/warnings and for build (`gcc`/`msvc`/`clang`) errors/warnings are created inline in the changed source files the build run for, e.g.: 30 | ![Annotation](./docs/imgs/annotation.png) 31 | - when necessary, it sets up the environment to build with the `MSVC` toolset. 32 | - automatic dump of log files created by `CMake` (e.g., `CMakeOutput.log`) and `vcpkg`. The content of those files flow into the workflow output log. The regex is customizable by the user. 33 | 34 | The provided [samples](#samples) use [GitHub hosted runners](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners). 35 | 36 | Good companions are the [run-vcpkg](https://github.com/marketplace/actions/run-vcpkg) action and the 37 | [get-cmake](https://github.com/marketplace/actions/get-cmake) action. 38 | 39 |
40 | 41 | ## Quickstart with instructions 42 | 43 | It is __highly recommended__ to use: 44 | - a [vcpkg.json](https://vcpkg.io/en/docs/maintainers/manifest-files.html) manifest file to declaratively specify the dependencies. 45 | - the required [CMakePresets.json](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) file. 46 | - [vcpkg as a submodule of your repository](https://github.com/microsoft/vcpkg/blob/master/README.md#vcpkg-as-a-submodule). 47 | 48 | ```yaml 49 | jobs: 50 | build: 51 | steps: 52 | #-uses: actions/cache@v3 <===== YOU DO NOT NEED THIS! 53 | 54 | # Install latest CMake and Ninja. 55 | - uses: lukka/get-cmake@latest 56 | # Or pin to a specific CMake version: 57 | # lukka/get-cmake@v3.27 58 | 59 | # Setup vcpkg: ensures vcpkg is downloaded and built. 60 | # Since vcpkg.json is being used later on to install the packages 61 | # when `run-cmake` runs, no packages are installed at this time 62 | # (and vcpkg does not run). 63 | - name: Setup anew (or from cache) vcpkg (and does not build any package) 64 | uses: lukka/run-vcpkg@v11 # Always specify the specific _version_ of the 65 | # action you need, `v11` in this case to stay up 66 | # to date with fixes on the v11 branch. 67 | #with: 68 | # This is the default location of the directory containing vcpkg sources. 69 | # Change it to the right location if needed. 70 | # vcpkgDirectory: '${{ github.workspace }}/vcpkg' 71 | 72 | # If not using a submodule for vcpkg sources, this specifies which commit 73 | # id must be checkout from a Git repo. 74 | # Note: it must not be set if using a Git submodule for vcpkg. 75 | # vcpkgGitCommitId: '${{ matrix.vcpkgCommitId }}' 76 | 77 | # This is only needed if the command `vcpkg install` must run at this step. 78 | # Instead it is highly suggested to let `run-cmake` to run vcpkg later on 79 | # using the vcpkg.cmake toolchain. The default is `false`. 80 | # runVcpkgInstall: true 81 | 82 | # This is only needed if `runVpkgInstall` is `true`. 83 | # This glob expression used to locate the vcpkg.json and use 84 | # its directory location as `working directory` when running `vcpkg install`. 85 | # Change it to match a single manifest file you want to use. 86 | # Note: do not use `${{ github.context }}` to compose the value as it 87 | # contains backslashes that would be misinterpreted. Instead 88 | # compose a value relative to the root of the repository using 89 | # `**/path/from/root/of/repo/to/vcpkg.json` to match the desired `vcpkg.json`. 90 | # vcpkgJsonGlob: '**/vcpkg.json' 91 | 92 | - name: Run CMake consuming CMakePreset.json and run vcpkg to build packages 93 | uses: lukka/run-cmake@v10 94 | with: 95 | # This is the default path to the CMakeLists.txt along side the 96 | # CMakePresets.json. Change if you need have CMakeLists.txt and CMakePresets.json 97 | # located elsewhere. 98 | # cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' 99 | 100 | # You could use CMake workflow presets defined in the CMakePresets.json 101 | # with just this line below. Note this one cannot be used with any other 102 | # preset input, it is mutually exclusive. 103 | # workflowPreset: 'workflow-name' 104 | 105 | # This is the name of the CMakePresets.json's configuration to use to generate 106 | # the project files. This configuration leverages the vcpkg.cmake toolchain file to 107 | # run vcpkg and install all dependencies specified in vcpkg.json. 108 | configurePreset: 'ninja-multi-vcpkg' 109 | # Additional arguments can be appended to the cmake command. 110 | # This is useful to reduce the number of CMake's Presets since you can reuse 111 | # an existing preset with different variables. 112 | configurePresetAdditionalArgs: "['-DENABLE_YOUR_FEATURE=1']" 113 | 114 | # This is the name of the CMakePresets.json's configuration to build the project. 115 | buildPreset: 'ninja-multi-vcpkg' 116 | # Additional arguments can be appended when building, for example to specify the 117 | # configuration to build. 118 | # This is useful to reduce the number of CMake's Presets you need in CMakePresets.json. 119 | buildPresetAdditionalArgs: "['--config Release']" 120 | 121 | # This is the name of the CMakePresets.json's configuration to test the project with. 122 | testPreset: 'ninja-multi-vcpkg' 123 | # Additional arguments can be appended when testing, for example to specify the config 124 | # to test. 125 | # This is useful to reduce the number of CMake's Presets you need in CMakePresets.json. 126 | testPresetAdditionalArgs: "['--config Release']" 127 | 128 | # [OPTIONAL] Define the vcpkg's triplet you want to enforce, otherwise the default one 129 | # for the hosting system will be automatically choosen (x64 is the default on all 130 | # platforms, e.g. `x64-osx`). 131 | # VCPKG_DEFAULT_TRIPLET: ${{ matrix.triplet }} 132 | # 133 | # [OPTIONAL] By default the action disables vcpkg's telemetry by defining VCPKG_DISABLE_METRICS. 134 | # This behavior can be disabled by defining `VCPKG_ENABLE_METRICS` as follows. 135 | # VCPKG_ENABLE_METRICS: 1 136 | # 137 | ``` 138 | 139 |
140 | 141 | ## Best practises 142 | 143 | ### Usage of github.workspace in multi platform workflows 144 | 145 | Using `github.workspace` may be challenging since it contains backslashes on Windows which are interpreted as escape sequences when used as base path of additional arguments. To work around this, you could use the `String.raw` function which prevents the escape sequences from being processed, e.g.: 146 | 147 | ````yaml 148 | with: 149 | configurePresetAdditionalArgs: "[ String.raw`-DD3D9_INCLUDE_DIR=${{ github.workspace }}/cache/Include` ]" 150 | ```` 151 | 152 |
153 | 154 | ## Action reference: all input/output parameters 155 | 156 | Description of all input parameters: 157 | [action.yml](https://github.com/lukka/run-cmake/blob/main/action.yml) 158 | 159 |
160 | 161 | ## Flowchart 162 | 163 | Flowchart with related input in [action.yml](https://github.com/lukka/run-cmake/blob/main/action.yml) which let customize the flow. 164 | 165 | ``` 166 | ┌───────────────────────────┐ ┌───────────────────────────┐ 167 | │ ├─────►│ and then exit │ - `cmakeListsTxtPath` 169 | └─────────────┬─────────────┘ └─────────────┬─────────────┘ - `workflowPreset` 170 | │ No ⬬ - `workflowPresetCmdString` 171 | ▼ 172 | ┌──────────────────────────────────┐ 173 | │ | - `cmakeListsTxtPath` 175 | | | - `configurePreset` 176 | │ $CONFIGURE_PRESET_NAME = │ - `configurePresetCmdString` 177 | │ configurePreset's value │ 178 | | runs: `cmake --preset` │ 179 | └─────────────┬────────────────────┘ 180 | ▼ 181 | ┌───────────────────────────────────┐ 182 | │ `*`│ Inputs: 183 | │ │ - `buildPreset` 184 | │ $BUILD_PRESET_NAME=buildPreset │ - `cmakeListsTxtPath` 185 | │ runs: `cmake --build --preset` │ - `buildPresetCmdString` 186 | └─────────────┬─────────────────────┘ - `buildPresetAdditionalArgs` 187 | ▼ 188 | ┌───────────────────────────────────┐ 189 | │ `*`│ Inputs: 190 | │ │ - `testPreset` 191 | │ $TEST_PRESET_NAME=testPreset │ - `cmakeListsTxtPath` 192 | │ runs: `ctest --preset` │ - `testPresetCmdString` 193 | └─────────────┬─────────────────────┘ - `testPresetAdditionalArgs` 194 | ▼ 195 | ┌───────────────────────────────────┐ 196 | │ `*`│ Inputs: 197 | │ │ - `packagePreset` 198 | │ $PACKAGE_PRESET_NAME=packagePreset│ - `cmakeListsTxtPath` 199 | │ runs: `cpack --preset` │ - `packagePresetCmdString` 200 | └─────────────┬─────────────────────┘ - `packagePresetAdditionalArgs` 201 | ▼ 202 | ⬬ END 203 | 204 | 205 | `*` On Windows runners, the MSVC environment is setup for each block 206 | with the `*` on the top right corner. Note that VCPKG_ROOT will not be 207 | overridden by the value defined in the VS Developer Command Prompt 208 | environment, but the original value will be kept. 209 | ┌───────────────────────────┐ 210 | │ │ - `runVcpkgEnvFormatString` 212 | │ run `vcpkg env` to set │ - `configurePresetAdditionalArgs` 213 | │ the environment for MSVC │ 214 | └───────────────────────────┘ 215 | ``` 216 | 217 |
218 | 219 | ## Samples 220 | 221 | _Checkmarks_ indicates whether the samples "uses" or specifies the thing in the header or whether it is true. 222 | 223 | |workflow link|`vcpkg` as submodule|explicit triplet|`vcpkg` toolchain|`CMake`'s Presets|`Ninja`|`run-vcpkg` runs vcpkg|`CMake` runs `vcpkg` 224 | |:-|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 225 | |[link](https://github.com/lukka/CppBuildTasks-Validation/blob/v10/.github/workflows/hosted-ninja-vcpkg_submod.yml)|✅|❌|✅|✅|✅|❌|✅| 226 | |[link](https://github.com/lukka/CppBuildTasks-Validation/blob/v10/.github/workflows/hosted-ninja-vcpkg.yml)|❌|❌|✅|✅|✅|❌|✅ 227 | |[link](https://github.com/lukka/CppBuildTasks-Validation/blob/v10/.github/workflows/hosted-ninja-vcpkg-install.yml)|❌|❌|✅|✅|✅|✅|❌| 228 | |[link](https://github.com/lukka/CppBuildTasks-Validation/blob/v10/.github/workflows/hosted-ninja-vcpkg_submod-triplet.yml)|✅|✅|✅|✅|✅|❌|✅ 229 | 230 |
231 | 232 | ## Who is using `run-cmake` 233 | 234 | [This graph](https://lukka.github.io/graph/graph.html) shows the list of public repositories with more than 25 stars using `run-cmake`. 235 | 236 |
237 | 238 | # License 239 | 240 | All the content in this repository is licensed under the [MIT License](LICENSE.txt). 241 | 242 | Copyright © 2019-2020-2021-2022-2023-2024-2025 Luca Cappa 243 | 244 |
245 | 246 | # Disclaimer 247 | 248 | The software is provided as is, there is no warranty of any kind. All users are encouraged to improve the [source code](https://github.com/lukka/run-cmake) with fixes and new features. 249 | 250 |
251 | 252 | # Contributing 253 | 254 | Read [CONTRIBUTING.md](CONTRIBUTING.md) 255 | 256 | -------------------------------------------------------------------------------- /dist/esprima.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Ariya Hidayat 3 | Copyright (C) 2013 Thaddee Tyl 4 | Copyright (C) 2013 Mathias Bynens 5 | Copyright (C) 2012 Ariya Hidayat 6 | Copyright (C) 2012 Mathias Bynens 7 | Copyright (C) 2012 Joost-Wim Boekesteijn 8 | Copyright (C) 2012 Kris Kowal 9 | Copyright (C) 2012 Yusuke Suzuki 10 | Copyright (C) 2012 Arpad Borsos 11 | Copyright (C) 2011 Ariya Hidayat 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright 17 | notice, this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 26 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /*jslint bitwise:true plusplus:true */ 35 | /*global esprima:true, define:true, exports:true, window: true, 36 | throwErrorTolerant: true, 37 | throwError: true, generateStatement: true, peek: true, 38 | parseAssignmentExpression: true, parseBlock: true, parseExpression: true, 39 | parseFunctionDeclaration: true, parseFunctionExpression: true, 40 | parseFunctionSourceElements: true, parseVariableIdentifier: true, 41 | parseLeftHandSideExpression: true, 42 | parseUnaryExpression: true, 43 | parseStatement: true, parseSourceElement: true */ 44 | 45 | (function (root, factory) { 46 | 'use strict'; 47 | 48 | // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, 49 | // Rhino, and plain browser loading. 50 | 51 | /* istanbul ignore next */ 52 | if (typeof define === 'function' && define.amd) { 53 | define(['exports'], factory); 54 | } else if (typeof exports !== 'undefined') { 55 | factory(exports); 56 | } else { 57 | factory((root.esprima = {})); 58 | } 59 | }(this, function (exports) { 60 | 'use strict'; 61 | 62 | var Token, 63 | TokenName, 64 | FnExprTokens, 65 | Syntax, 66 | PropertyKind, 67 | Messages, 68 | Regex, 69 | SyntaxTreeDelegate, 70 | source, 71 | strict, 72 | index, 73 | lineNumber, 74 | lineStart, 75 | length, 76 | delegate, 77 | lookahead, 78 | state, 79 | extra; 80 | 81 | Token = { 82 | BooleanLiteral: 1, 83 | EOF: 2, 84 | Identifier: 3, 85 | Keyword: 4, 86 | NullLiteral: 5, 87 | NumericLiteral: 6, 88 | Punctuator: 7, 89 | StringLiteral: 8, 90 | RegularExpression: 9 91 | }; 92 | 93 | TokenName = {}; 94 | TokenName[Token.BooleanLiteral] = 'Boolean'; 95 | TokenName[Token.EOF] = ''; 96 | TokenName[Token.Identifier] = 'Identifier'; 97 | TokenName[Token.Keyword] = 'Keyword'; 98 | TokenName[Token.NullLiteral] = 'Null'; 99 | TokenName[Token.NumericLiteral] = 'Numeric'; 100 | TokenName[Token.Punctuator] = 'Punctuator'; 101 | TokenName[Token.StringLiteral] = 'String'; 102 | TokenName[Token.RegularExpression] = 'RegularExpression'; 103 | 104 | // A function following one of those tokens is an expression. 105 | FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', 106 | 'return', 'case', 'delete', 'throw', 'void', 107 | // assignment operators 108 | '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', 109 | '&=', '|=', '^=', ',', 110 | // binary/unary operators 111 | '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', 112 | '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', 113 | '<=', '<', '>', '!=', '!==']; 114 | 115 | Syntax = { 116 | AssignmentExpression: 'AssignmentExpression', 117 | ArrayExpression: 'ArrayExpression', 118 | BlockStatement: 'BlockStatement', 119 | BinaryExpression: 'BinaryExpression', 120 | BreakStatement: 'BreakStatement', 121 | CallExpression: 'CallExpression', 122 | CatchClause: 'CatchClause', 123 | ConditionalExpression: 'ConditionalExpression', 124 | ContinueStatement: 'ContinueStatement', 125 | DoWhileStatement: 'DoWhileStatement', 126 | DebuggerStatement: 'DebuggerStatement', 127 | EmptyStatement: 'EmptyStatement', 128 | ExpressionStatement: 'ExpressionStatement', 129 | ForStatement: 'ForStatement', 130 | ForInStatement: 'ForInStatement', 131 | FunctionDeclaration: 'FunctionDeclaration', 132 | FunctionExpression: 'FunctionExpression', 133 | Identifier: 'Identifier', 134 | IfStatement: 'IfStatement', 135 | Literal: 'Literal', 136 | LabeledStatement: 'LabeledStatement', 137 | LogicalExpression: 'LogicalExpression', 138 | MemberExpression: 'MemberExpression', 139 | NewExpression: 'NewExpression', 140 | ObjectExpression: 'ObjectExpression', 141 | Program: 'Program', 142 | Property: 'Property', 143 | ReturnStatement: 'ReturnStatement', 144 | SequenceExpression: 'SequenceExpression', 145 | SwitchStatement: 'SwitchStatement', 146 | SwitchCase: 'SwitchCase', 147 | ThisExpression: 'ThisExpression', 148 | ThrowStatement: 'ThrowStatement', 149 | TryStatement: 'TryStatement', 150 | UnaryExpression: 'UnaryExpression', 151 | UpdateExpression: 'UpdateExpression', 152 | VariableDeclaration: 'VariableDeclaration', 153 | VariableDeclarator: 'VariableDeclarator', 154 | WhileStatement: 'WhileStatement', 155 | WithStatement: 'WithStatement' 156 | }; 157 | 158 | PropertyKind = { 159 | Data: 1, 160 | Get: 2, 161 | Set: 4 162 | }; 163 | 164 | // Error messages should be identical to V8. 165 | Messages = { 166 | UnexpectedToken: 'Unexpected token %0', 167 | UnexpectedNumber: 'Unexpected number', 168 | UnexpectedString: 'Unexpected string', 169 | UnexpectedIdentifier: 'Unexpected identifier', 170 | UnexpectedReserved: 'Unexpected reserved word', 171 | UnexpectedEOS: 'Unexpected end of input', 172 | NewlineAfterThrow: 'Illegal newline after throw', 173 | InvalidRegExp: 'Invalid regular expression', 174 | UnterminatedRegExp: 'Invalid regular expression: missing /', 175 | InvalidLHSInAssignment: 'Invalid left-hand side in assignment', 176 | InvalidLHSInForIn: 'Invalid left-hand side in for-in', 177 | MultipleDefaultsInSwitch: 'More than one default clause in switch statement', 178 | NoCatchOrFinally: 'Missing catch or finally after try', 179 | UnknownLabel: 'Undefined label \'%0\'', 180 | Redeclaration: '%0 \'%1\' has already been declared', 181 | IllegalContinue: 'Illegal continue statement', 182 | IllegalBreak: 'Illegal break statement', 183 | IllegalReturn: 'Illegal return statement', 184 | StrictModeWith: 'Strict mode code may not include a with statement', 185 | StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', 186 | StrictVarName: 'Variable name may not be eval or arguments in strict mode', 187 | StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', 188 | StrictParamDupe: 'Strict mode function may not have duplicate parameter names', 189 | StrictFunctionName: 'Function name may not be eval or arguments in strict mode', 190 | StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', 191 | StrictDelete: 'Delete of an unqualified identifier in strict mode.', 192 | StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode', 193 | AccessorDataProperty: 'Object literal may not have data and accessor property with the same name', 194 | AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name', 195 | StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', 196 | StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', 197 | StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', 198 | StrictReservedWord: 'Use of future reserved word in strict mode' 199 | }; 200 | 201 | // See also tools/generate-unicode-regex.py. 202 | Regex = { 203 | NonAsciiIdentifierStart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]'), 204 | NonAsciiIdentifierPart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]') 205 | }; 206 | 207 | // Ensure the condition is true, otherwise throw an error. 208 | // This is only to have a better contract semantic, i.e. another safety net 209 | // to catch a logic error. The condition shall be fulfilled in normal case. 210 | // Do NOT use this to enforce a certain condition on any user input. 211 | 212 | function assert(condition, message) { 213 | /* istanbul ignore if */ 214 | if (!condition) { 215 | throw new Error('ASSERT: ' + message); 216 | } 217 | } 218 | 219 | function isDecimalDigit(ch) { 220 | return (ch >= 48 && ch <= 57); // 0..9 221 | } 222 | 223 | function isHexDigit(ch) { 224 | return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; 225 | } 226 | 227 | function isOctalDigit(ch) { 228 | return '01234567'.indexOf(ch) >= 0; 229 | } 230 | 231 | 232 | // 7.2 White Space 233 | 234 | function isWhiteSpace(ch) { 235 | return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || 236 | (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0); 237 | } 238 | 239 | // 7.3 Line Terminators 240 | 241 | function isLineTerminator(ch) { 242 | return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029); 243 | } 244 | 245 | // 7.6 Identifier Names and Identifiers 246 | 247 | function isIdentifierStart(ch) { 248 | return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) 249 | (ch >= 0x41 && ch <= 0x5A) || // A..Z 250 | (ch >= 0x61 && ch <= 0x7A) || // a..z 251 | (ch === 0x5C) || // \ (backslash) 252 | ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch))); 253 | } 254 | 255 | function isIdentifierPart(ch) { 256 | return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) 257 | (ch >= 0x41 && ch <= 0x5A) || // A..Z 258 | (ch >= 0x61 && ch <= 0x7A) || // a..z 259 | (ch >= 0x30 && ch <= 0x39) || // 0..9 260 | (ch === 0x5C) || // \ (backslash) 261 | ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch))); 262 | } 263 | 264 | // 7.6.1.2 Future Reserved Words 265 | 266 | function isFutureReservedWord(id) { 267 | switch (id) { 268 | case 'class': 269 | case 'enum': 270 | case 'export': 271 | case 'extends': 272 | case 'import': 273 | case 'super': 274 | return true; 275 | default: 276 | return false; 277 | } 278 | } 279 | 280 | function isStrictModeReservedWord(id) { 281 | switch (id) { 282 | case 'implements': 283 | case 'interface': 284 | case 'package': 285 | case 'private': 286 | case 'protected': 287 | case 'public': 288 | case 'static': 289 | case 'yield': 290 | case 'let': 291 | return true; 292 | default: 293 | return false; 294 | } 295 | } 296 | 297 | function isRestrictedWord(id) { 298 | return id === 'eval' || id === 'arguments'; 299 | } 300 | 301 | // 7.6.1.1 Keywords 302 | 303 | function isKeyword(id) { 304 | if (strict && isStrictModeReservedWord(id)) { 305 | return true; 306 | } 307 | 308 | // 'const' is specialized as Keyword in V8. 309 | // 'yield' and 'let' are for compatiblity with SpiderMonkey and ES.next. 310 | // Some others are from future reserved words. 311 | 312 | switch (id.length) { 313 | case 2: 314 | return (id === 'if') || (id === 'in') || (id === 'do'); 315 | case 3: 316 | return (id === 'var') || (id === 'for') || (id === 'new') || 317 | (id === 'try') || (id === 'let'); 318 | case 4: 319 | return (id === 'this') || (id === 'else') || (id === 'case') || 320 | (id === 'void') || (id === 'with') || (id === 'enum'); 321 | case 5: 322 | return (id === 'while') || (id === 'break') || (id === 'catch') || 323 | (id === 'throw') || (id === 'const') || (id === 'yield') || 324 | (id === 'class') || (id === 'super'); 325 | case 6: 326 | return (id === 'return') || (id === 'typeof') || (id === 'delete') || 327 | (id === 'switch') || (id === 'export') || (id === 'import'); 328 | case 7: 329 | return (id === 'default') || (id === 'finally') || (id === 'extends'); 330 | case 8: 331 | return (id === 'function') || (id === 'continue') || (id === 'debugger'); 332 | case 10: 333 | return (id === 'instanceof'); 334 | default: 335 | return false; 336 | } 337 | } 338 | 339 | // 7.4 Comments 340 | 341 | function addComment(type, value, start, end, loc) { 342 | var comment, attacher; 343 | 344 | assert(typeof start === 'number', 'Comment must have valid position'); 345 | 346 | // Because the way the actual token is scanned, often the comments 347 | // (if any) are skipped twice during the lexical analysis. 348 | // Thus, we need to skip adding a comment if the comment array already 349 | // handled it. 350 | if (state.lastCommentStart >= start) { 351 | return; 352 | } 353 | state.lastCommentStart = start; 354 | 355 | comment = { 356 | type: type, 357 | value: value 358 | }; 359 | if (extra.range) { 360 | comment.range = [start, end]; 361 | } 362 | if (extra.loc) { 363 | comment.loc = loc; 364 | } 365 | extra.comments.push(comment); 366 | if (extra.attachComment) { 367 | extra.leadingComments.push(comment); 368 | extra.trailingComments.push(comment); 369 | } 370 | } 371 | 372 | function skipSingleLineComment(offset) { 373 | var start, loc, ch, comment; 374 | 375 | start = index - offset; 376 | loc = { 377 | start: { 378 | line: lineNumber, 379 | column: index - lineStart - offset 380 | } 381 | }; 382 | 383 | while (index < length) { 384 | ch = source.charCodeAt(index); 385 | ++index; 386 | if (isLineTerminator(ch)) { 387 | if (extra.comments) { 388 | comment = source.slice(start + offset, index - 1); 389 | loc.end = { 390 | line: lineNumber, 391 | column: index - lineStart - 1 392 | }; 393 | addComment('Line', comment, start, index - 1, loc); 394 | } 395 | if (ch === 13 && source.charCodeAt(index) === 10) { 396 | ++index; 397 | } 398 | ++lineNumber; 399 | lineStart = index; 400 | return; 401 | } 402 | } 403 | 404 | if (extra.comments) { 405 | comment = source.slice(start + offset, index); 406 | loc.end = { 407 | line: lineNumber, 408 | column: index - lineStart 409 | }; 410 | addComment('Line', comment, start, index, loc); 411 | } 412 | } 413 | 414 | function skipMultiLineComment() { 415 | var start, loc, ch, comment; 416 | 417 | if (extra.comments) { 418 | start = index - 2; 419 | loc = { 420 | start: { 421 | line: lineNumber, 422 | column: index - lineStart - 2 423 | } 424 | }; 425 | } 426 | 427 | while (index < length) { 428 | ch = source.charCodeAt(index); 429 | if (isLineTerminator(ch)) { 430 | if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) { 431 | ++index; 432 | } 433 | ++lineNumber; 434 | ++index; 435 | lineStart = index; 436 | if (index >= length) { 437 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 438 | } 439 | } else if (ch === 0x2A) { 440 | // Block comment ends with '*/'. 441 | if (source.charCodeAt(index + 1) === 0x2F) { 442 | ++index; 443 | ++index; 444 | if (extra.comments) { 445 | comment = source.slice(start + 2, index - 2); 446 | loc.end = { 447 | line: lineNumber, 448 | column: index - lineStart 449 | }; 450 | addComment('Block', comment, start, index, loc); 451 | } 452 | return; 453 | } 454 | ++index; 455 | } else { 456 | ++index; 457 | } 458 | } 459 | 460 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 461 | } 462 | 463 | function skipComment() { 464 | var ch, start; 465 | 466 | start = (index === 0); 467 | while (index < length) { 468 | ch = source.charCodeAt(index); 469 | 470 | if (isWhiteSpace(ch)) { 471 | ++index; 472 | } else if (isLineTerminator(ch)) { 473 | ++index; 474 | if (ch === 0x0D && source.charCodeAt(index) === 0x0A) { 475 | ++index; 476 | } 477 | ++lineNumber; 478 | lineStart = index; 479 | start = true; 480 | } else if (ch === 0x2F) { // U+002F is '/' 481 | ch = source.charCodeAt(index + 1); 482 | if (ch === 0x2F) { 483 | ++index; 484 | ++index; 485 | skipSingleLineComment(2); 486 | start = true; 487 | } else if (ch === 0x2A) { // U+002A is '*' 488 | ++index; 489 | ++index; 490 | skipMultiLineComment(); 491 | } else { 492 | break; 493 | } 494 | } else if (start && ch === 0x2D) { // U+002D is '-' 495 | // U+003E is '>' 496 | if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) { 497 | // '-->' is a single-line comment 498 | index += 3; 499 | skipSingleLineComment(3); 500 | } else { 501 | break; 502 | } 503 | } else if (ch === 0x3C) { // U+003C is '<' 504 | if (source.slice(index + 1, index + 4) === '!--') { 505 | ++index; // `<` 506 | ++index; // `!` 507 | ++index; // `-` 508 | ++index; // `-` 509 | skipSingleLineComment(4); 510 | } else { 511 | break; 512 | } 513 | } else { 514 | break; 515 | } 516 | } 517 | } 518 | 519 | function scanHexEscape(prefix) { 520 | var i, len, ch, code = 0; 521 | 522 | len = (prefix === 'u') ? 4 : 2; 523 | for (i = 0; i < len; ++i) { 524 | if (index < length && isHexDigit(source[index])) { 525 | ch = source[index++]; 526 | code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); 527 | } else { 528 | return ''; 529 | } 530 | } 531 | return String.fromCharCode(code); 532 | } 533 | 534 | function getEscapedIdentifier() { 535 | var ch, id; 536 | 537 | ch = source.charCodeAt(index++); 538 | id = String.fromCharCode(ch); 539 | 540 | // '\u' (U+005C, U+0075) denotes an escaped character. 541 | if (ch === 0x5C) { 542 | if (source.charCodeAt(index) !== 0x75) { 543 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 544 | } 545 | ++index; 546 | ch = scanHexEscape('u'); 547 | if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) { 548 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 549 | } 550 | id = ch; 551 | } 552 | 553 | while (index < length) { 554 | ch = source.charCodeAt(index); 555 | if (!isIdentifierPart(ch)) { 556 | break; 557 | } 558 | ++index; 559 | id += String.fromCharCode(ch); 560 | 561 | // '\u' (U+005C, U+0075) denotes an escaped character. 562 | if (ch === 0x5C) { 563 | id = id.substr(0, id.length - 1); 564 | if (source.charCodeAt(index) !== 0x75) { 565 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 566 | } 567 | ++index; 568 | ch = scanHexEscape('u'); 569 | if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) { 570 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 571 | } 572 | id += ch; 573 | } 574 | } 575 | 576 | return id; 577 | } 578 | 579 | function getIdentifier() { 580 | var start, ch; 581 | 582 | start = index++; 583 | while (index < length) { 584 | ch = source.charCodeAt(index); 585 | if (ch === 0x5C) { 586 | // Blackslash (U+005C) marks Unicode escape sequence. 587 | index = start; 588 | return getEscapedIdentifier(); 589 | } 590 | if (isIdentifierPart(ch)) { 591 | ++index; 592 | } else { 593 | break; 594 | } 595 | } 596 | 597 | return source.slice(start, index); 598 | } 599 | 600 | function scanIdentifier() { 601 | var start, id, type; 602 | 603 | start = index; 604 | 605 | // Backslash (U+005C) starts an escaped character. 606 | id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier(); 607 | 608 | // There is no keyword or literal with only one character. 609 | // Thus, it must be an identifier. 610 | if (id.length === 1) { 611 | type = Token.Identifier; 612 | } else if (isKeyword(id)) { 613 | type = Token.Keyword; 614 | } else if (id === 'null') { 615 | type = Token.NullLiteral; 616 | } else if (id === 'true' || id === 'false') { 617 | type = Token.BooleanLiteral; 618 | } else { 619 | type = Token.Identifier; 620 | } 621 | 622 | return { 623 | type: type, 624 | value: id, 625 | lineNumber: lineNumber, 626 | lineStart: lineStart, 627 | start: start, 628 | end: index 629 | }; 630 | } 631 | 632 | 633 | // 7.7 Punctuators 634 | 635 | function scanPunctuator() { 636 | var start = index, 637 | code = source.charCodeAt(index), 638 | code2, 639 | ch1 = source[index], 640 | ch2, 641 | ch3, 642 | ch4; 643 | 644 | switch (code) { 645 | 646 | // Check for most common single-character punctuators. 647 | case 0x2E: // . dot 648 | case 0x28: // ( open bracket 649 | case 0x29: // ) close bracket 650 | case 0x3B: // ; semicolon 651 | case 0x2C: // , comma 652 | case 0x7B: // { open curly brace 653 | case 0x7D: // } close curly brace 654 | case 0x5B: // [ 655 | case 0x5D: // ] 656 | case 0x3A: // : 657 | case 0x3F: // ? 658 | case 0x7E: // ~ 659 | ++index; 660 | if (extra.tokenize) { 661 | if (code === 0x28) { 662 | extra.openParenToken = extra.tokens.length; 663 | } else if (code === 0x7B) { 664 | extra.openCurlyToken = extra.tokens.length; 665 | } 666 | } 667 | return { 668 | type: Token.Punctuator, 669 | value: String.fromCharCode(code), 670 | lineNumber: lineNumber, 671 | lineStart: lineStart, 672 | start: start, 673 | end: index 674 | }; 675 | 676 | default: 677 | code2 = source.charCodeAt(index + 1); 678 | 679 | // '=' (U+003D) marks an assignment or comparison operator. 680 | if (code2 === 0x3D) { 681 | switch (code) { 682 | case 0x2B: // + 683 | case 0x2D: // - 684 | case 0x2F: // / 685 | case 0x3C: // < 686 | case 0x3E: // > 687 | case 0x5E: // ^ 688 | case 0x7C: // | 689 | case 0x25: // % 690 | case 0x26: // & 691 | case 0x2A: // * 692 | index += 2; 693 | return { 694 | type: Token.Punctuator, 695 | value: String.fromCharCode(code) + String.fromCharCode(code2), 696 | lineNumber: lineNumber, 697 | lineStart: lineStart, 698 | start: start, 699 | end: index 700 | }; 701 | 702 | case 0x21: // ! 703 | case 0x3D: // = 704 | index += 2; 705 | 706 | // !== and === 707 | if (source.charCodeAt(index) === 0x3D) { 708 | ++index; 709 | } 710 | return { 711 | type: Token.Punctuator, 712 | value: source.slice(start, index), 713 | lineNumber: lineNumber, 714 | lineStart: lineStart, 715 | start: start, 716 | end: index 717 | }; 718 | } 719 | } 720 | } 721 | 722 | // 4-character punctuator: >>>= 723 | 724 | ch4 = source.substr(index, 4); 725 | 726 | if (ch4 === '>>>=') { 727 | index += 4; 728 | return { 729 | type: Token.Punctuator, 730 | value: ch4, 731 | lineNumber: lineNumber, 732 | lineStart: lineStart, 733 | start: start, 734 | end: index 735 | }; 736 | } 737 | 738 | // 3-character punctuators: === !== >>> <<= >>= 739 | 740 | ch3 = ch4.substr(0, 3); 741 | 742 | if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') { 743 | index += 3; 744 | return { 745 | type: Token.Punctuator, 746 | value: ch3, 747 | lineNumber: lineNumber, 748 | lineStart: lineStart, 749 | start: start, 750 | end: index 751 | }; 752 | } 753 | 754 | // Other 2-character punctuators: ++ -- << >> && || 755 | ch2 = ch3.substr(0, 2); 756 | 757 | if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') { 758 | index += 2; 759 | return { 760 | type: Token.Punctuator, 761 | value: ch2, 762 | lineNumber: lineNumber, 763 | lineStart: lineStart, 764 | start: start, 765 | end: index 766 | }; 767 | } 768 | 769 | // 1-character punctuators: < > = ! + - * % & | ^ / 770 | if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { 771 | ++index; 772 | return { 773 | type: Token.Punctuator, 774 | value: ch1, 775 | lineNumber: lineNumber, 776 | lineStart: lineStart, 777 | start: start, 778 | end: index 779 | }; 780 | } 781 | 782 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 783 | } 784 | 785 | // 7.8.3 Numeric Literals 786 | 787 | function scanHexLiteral(start) { 788 | var number = ''; 789 | 790 | while (index < length) { 791 | if (!isHexDigit(source[index])) { 792 | break; 793 | } 794 | number += source[index++]; 795 | } 796 | 797 | if (number.length === 0) { 798 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 799 | } 800 | 801 | if (isIdentifierStart(source.charCodeAt(index))) { 802 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 803 | } 804 | 805 | return { 806 | type: Token.NumericLiteral, 807 | value: parseInt('0x' + number, 16), 808 | lineNumber: lineNumber, 809 | lineStart: lineStart, 810 | start: start, 811 | end: index 812 | }; 813 | } 814 | 815 | function scanOctalLiteral(start) { 816 | var number = '0' + source[index++]; 817 | while (index < length) { 818 | if (!isOctalDigit(source[index])) { 819 | break; 820 | } 821 | number += source[index++]; 822 | } 823 | 824 | if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { 825 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 826 | } 827 | 828 | return { 829 | type: Token.NumericLiteral, 830 | value: parseInt(number, 8), 831 | octal: true, 832 | lineNumber: lineNumber, 833 | lineStart: lineStart, 834 | start: start, 835 | end: index 836 | }; 837 | } 838 | 839 | function scanNumericLiteral() { 840 | var number, start, ch; 841 | 842 | ch = source[index]; 843 | assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), 844 | 'Numeric literal must start with a decimal digit or a decimal point'); 845 | 846 | start = index; 847 | number = ''; 848 | if (ch !== '.') { 849 | number = source[index++]; 850 | ch = source[index]; 851 | 852 | // Hex number starts with '0x'. 853 | // Octal number starts with '0'. 854 | if (number === '0') { 855 | if (ch === 'x' || ch === 'X') { 856 | ++index; 857 | return scanHexLiteral(start); 858 | } 859 | if (isOctalDigit(ch)) { 860 | return scanOctalLiteral(start); 861 | } 862 | 863 | // decimal number starts with '0' such as '09' is illegal. 864 | if (ch && isDecimalDigit(ch.charCodeAt(0))) { 865 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 866 | } 867 | } 868 | 869 | while (isDecimalDigit(source.charCodeAt(index))) { 870 | number += source[index++]; 871 | } 872 | ch = source[index]; 873 | } 874 | 875 | if (ch === '.') { 876 | number += source[index++]; 877 | while (isDecimalDigit(source.charCodeAt(index))) { 878 | number += source[index++]; 879 | } 880 | ch = source[index]; 881 | } 882 | 883 | if (ch === 'e' || ch === 'E') { 884 | number += source[index++]; 885 | 886 | ch = source[index]; 887 | if (ch === '+' || ch === '-') { 888 | number += source[index++]; 889 | } 890 | if (isDecimalDigit(source.charCodeAt(index))) { 891 | while (isDecimalDigit(source.charCodeAt(index))) { 892 | number += source[index++]; 893 | } 894 | } else { 895 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 896 | } 897 | } 898 | 899 | if (isIdentifierStart(source.charCodeAt(index))) { 900 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 901 | } 902 | 903 | return { 904 | type: Token.NumericLiteral, 905 | value: parseFloat(number), 906 | lineNumber: lineNumber, 907 | lineStart: lineStart, 908 | start: start, 909 | end: index 910 | }; 911 | } 912 | 913 | // 7.8.4 String Literals 914 | 915 | function scanStringLiteral() { 916 | var str = '', quote, start, ch, code, unescaped, restore, octal = false, startLineNumber, startLineStart; 917 | startLineNumber = lineNumber; 918 | startLineStart = lineStart; 919 | 920 | quote = source[index]; 921 | assert((quote === '\'' || quote === '"'), 922 | 'String literal must starts with a quote'); 923 | 924 | start = index; 925 | ++index; 926 | 927 | while (index < length) { 928 | ch = source[index++]; 929 | 930 | if (ch === quote) { 931 | quote = ''; 932 | break; 933 | } else if (ch === '\\') { 934 | ch = source[index++]; 935 | if (!ch || !isLineTerminator(ch.charCodeAt(0))) { 936 | switch (ch) { 937 | case 'u': 938 | case 'x': 939 | restore = index; 940 | unescaped = scanHexEscape(ch); 941 | if (unescaped) { 942 | str += unescaped; 943 | } else { 944 | index = restore; 945 | str += ch; 946 | } 947 | break; 948 | case 'n': 949 | str += '\n'; 950 | break; 951 | case 'r': 952 | str += '\r'; 953 | break; 954 | case 't': 955 | str += '\t'; 956 | break; 957 | case 'b': 958 | str += '\b'; 959 | break; 960 | case 'f': 961 | str += '\f'; 962 | break; 963 | case 'v': 964 | str += '\x0B'; 965 | break; 966 | 967 | default: 968 | if (isOctalDigit(ch)) { 969 | code = '01234567'.indexOf(ch); 970 | 971 | // \0 is not octal escape sequence 972 | if (code !== 0) { 973 | octal = true; 974 | } 975 | 976 | if (index < length && isOctalDigit(source[index])) { 977 | octal = true; 978 | code = code * 8 + '01234567'.indexOf(source[index++]); 979 | 980 | // 3 digits are only allowed when string starts 981 | // with 0, 1, 2, 3 982 | if ('0123'.indexOf(ch) >= 0 && 983 | index < length && 984 | isOctalDigit(source[index])) { 985 | code = code * 8 + '01234567'.indexOf(source[index++]); 986 | } 987 | } 988 | str += String.fromCharCode(code); 989 | } else { 990 | str += ch; 991 | } 992 | break; 993 | } 994 | } else { 995 | ++lineNumber; 996 | if (ch === '\r' && source[index] === '\n') { 997 | ++index; 998 | } 999 | lineStart = index; 1000 | } 1001 | } else if (isLineTerminator(ch.charCodeAt(0))) { 1002 | break; 1003 | } else { 1004 | str += ch; 1005 | } 1006 | } 1007 | 1008 | if (quote !== '') { 1009 | throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); 1010 | } 1011 | 1012 | return { 1013 | type: Token.StringLiteral, 1014 | value: str, 1015 | octal: octal, 1016 | startLineNumber: startLineNumber, 1017 | startLineStart: startLineStart, 1018 | lineNumber: lineNumber, 1019 | lineStart: lineStart, 1020 | start: start, 1021 | end: index 1022 | }; 1023 | } 1024 | 1025 | function testRegExp(pattern, flags) { 1026 | var value; 1027 | try { 1028 | value = new RegExp(pattern, flags); 1029 | } catch (e) { 1030 | throwError({}, Messages.InvalidRegExp); 1031 | } 1032 | return value; 1033 | } 1034 | 1035 | function scanRegExpBody() { 1036 | var ch, str, classMarker, terminated, body; 1037 | 1038 | ch = source[index]; 1039 | assert(ch === '/', 'Regular expression literal must start with a slash'); 1040 | str = source[index++]; 1041 | 1042 | classMarker = false; 1043 | terminated = false; 1044 | while (index < length) { 1045 | ch = source[index++]; 1046 | str += ch; 1047 | if (ch === '\\') { 1048 | ch = source[index++]; 1049 | // ECMA-262 7.8.5 1050 | if (isLineTerminator(ch.charCodeAt(0))) { 1051 | throwError({}, Messages.UnterminatedRegExp); 1052 | } 1053 | str += ch; 1054 | } else if (isLineTerminator(ch.charCodeAt(0))) { 1055 | throwError({}, Messages.UnterminatedRegExp); 1056 | } else if (classMarker) { 1057 | if (ch === ']') { 1058 | classMarker = false; 1059 | } 1060 | } else { 1061 | if (ch === '/') { 1062 | terminated = true; 1063 | break; 1064 | } else if (ch === '[') { 1065 | classMarker = true; 1066 | } 1067 | } 1068 | } 1069 | 1070 | if (!terminated) { 1071 | throwError({}, Messages.UnterminatedRegExp); 1072 | } 1073 | 1074 | // Exclude leading and trailing slash. 1075 | body = str.substr(1, str.length - 2); 1076 | return { 1077 | value: body, 1078 | literal: str 1079 | }; 1080 | } 1081 | 1082 | function scanRegExpFlags() { 1083 | var ch, str, flags, restore; 1084 | 1085 | str = ''; 1086 | flags = ''; 1087 | while (index < length) { 1088 | ch = source[index]; 1089 | if (!isIdentifierPart(ch.charCodeAt(0))) { 1090 | break; 1091 | } 1092 | 1093 | ++index; 1094 | if (ch === '\\' && index < length) { 1095 | ch = source[index]; 1096 | if (ch === 'u') { 1097 | ++index; 1098 | restore = index; 1099 | ch = scanHexEscape('u'); 1100 | if (ch) { 1101 | flags += ch; 1102 | for (str += '\\u'; restore < index; ++restore) { 1103 | str += source[restore]; 1104 | } 1105 | } else { 1106 | index = restore; 1107 | flags += 'u'; 1108 | str += '\\u'; 1109 | } 1110 | throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); 1111 | } else { 1112 | str += '\\'; 1113 | throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL'); 1114 | } 1115 | } else { 1116 | flags += ch; 1117 | str += ch; 1118 | } 1119 | } 1120 | 1121 | return { 1122 | value: flags, 1123 | literal: str 1124 | }; 1125 | } 1126 | 1127 | function scanRegExp() { 1128 | var start, body, flags, pattern, value; 1129 | 1130 | lookahead = null; 1131 | skipComment(); 1132 | start = index; 1133 | 1134 | body = scanRegExpBody(); 1135 | flags = scanRegExpFlags(); 1136 | value = testRegExp(body.value, flags.value); 1137 | 1138 | if (extra.tokenize) { 1139 | return { 1140 | type: Token.RegularExpression, 1141 | value: value, 1142 | lineNumber: lineNumber, 1143 | lineStart: lineStart, 1144 | start: start, 1145 | end: index 1146 | }; 1147 | } 1148 | 1149 | return { 1150 | literal: body.literal + flags.literal, 1151 | value: value, 1152 | start: start, 1153 | end: index 1154 | }; 1155 | } 1156 | 1157 | function collectRegex() { 1158 | var pos, loc, regex, token; 1159 | 1160 | skipComment(); 1161 | 1162 | pos = index; 1163 | loc = { 1164 | start: { 1165 | line: lineNumber, 1166 | column: index - lineStart 1167 | } 1168 | }; 1169 | 1170 | regex = scanRegExp(); 1171 | loc.end = { 1172 | line: lineNumber, 1173 | column: index - lineStart 1174 | }; 1175 | 1176 | /* istanbul ignore next */ 1177 | if (!extra.tokenize) { 1178 | // Pop the previous token, which is likely '/' or '/=' 1179 | if (extra.tokens.length > 0) { 1180 | token = extra.tokens[extra.tokens.length - 1]; 1181 | if (token.range[0] === pos && token.type === 'Punctuator') { 1182 | if (token.value === '/' || token.value === '/=') { 1183 | extra.tokens.pop(); 1184 | } 1185 | } 1186 | } 1187 | 1188 | extra.tokens.push({ 1189 | type: 'RegularExpression', 1190 | value: regex.literal, 1191 | range: [pos, index], 1192 | loc: loc 1193 | }); 1194 | } 1195 | 1196 | return regex; 1197 | } 1198 | 1199 | function isIdentifierName(token) { 1200 | return token.type === Token.Identifier || 1201 | token.type === Token.Keyword || 1202 | token.type === Token.BooleanLiteral || 1203 | token.type === Token.NullLiteral; 1204 | } 1205 | 1206 | function advanceSlash() { 1207 | var prevToken, 1208 | checkToken; 1209 | // Using the following algorithm: 1210 | // https://github.com/mozilla/sweet.js/wiki/design 1211 | prevToken = extra.tokens[extra.tokens.length - 1]; 1212 | if (!prevToken) { 1213 | // Nothing before that: it cannot be a division. 1214 | return collectRegex(); 1215 | } 1216 | if (prevToken.type === 'Punctuator') { 1217 | if (prevToken.value === ']') { 1218 | return scanPunctuator(); 1219 | } 1220 | if (prevToken.value === ')') { 1221 | checkToken = extra.tokens[extra.openParenToken - 1]; 1222 | if (checkToken && 1223 | checkToken.type === 'Keyword' && 1224 | (checkToken.value === 'if' || 1225 | checkToken.value === 'while' || 1226 | checkToken.value === 'for' || 1227 | checkToken.value === 'with')) { 1228 | return collectRegex(); 1229 | } 1230 | return scanPunctuator(); 1231 | } 1232 | if (prevToken.value === '}') { 1233 | // Dividing a function by anything makes little sense, 1234 | // but we have to check for that. 1235 | if (extra.tokens[extra.openCurlyToken - 3] && 1236 | extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') { 1237 | // Anonymous function. 1238 | checkToken = extra.tokens[extra.openCurlyToken - 4]; 1239 | if (!checkToken) { 1240 | return scanPunctuator(); 1241 | } 1242 | } else if (extra.tokens[extra.openCurlyToken - 4] && 1243 | extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') { 1244 | // Named function. 1245 | checkToken = extra.tokens[extra.openCurlyToken - 5]; 1246 | if (!checkToken) { 1247 | return collectRegex(); 1248 | } 1249 | } else { 1250 | return scanPunctuator(); 1251 | } 1252 | // checkToken determines whether the function is 1253 | // a declaration or an expression. 1254 | if (FnExprTokens.indexOf(checkToken.value) >= 0) { 1255 | // It is an expression. 1256 | return scanPunctuator(); 1257 | } 1258 | // It is a declaration. 1259 | return collectRegex(); 1260 | } 1261 | return collectRegex(); 1262 | } 1263 | if (prevToken.type === 'Keyword') { 1264 | return collectRegex(); 1265 | } 1266 | return scanPunctuator(); 1267 | } 1268 | 1269 | function advance() { 1270 | var ch; 1271 | 1272 | skipComment(); 1273 | 1274 | if (index >= length) { 1275 | return { 1276 | type: Token.EOF, 1277 | lineNumber: lineNumber, 1278 | lineStart: lineStart, 1279 | start: index, 1280 | end: index 1281 | }; 1282 | } 1283 | 1284 | ch = source.charCodeAt(index); 1285 | 1286 | if (isIdentifierStart(ch)) { 1287 | return scanIdentifier(); 1288 | } 1289 | 1290 | // Very common: ( and ) and ; 1291 | if (ch === 0x28 || ch === 0x29 || ch === 0x3B) { 1292 | return scanPunctuator(); 1293 | } 1294 | 1295 | // String literal starts with single quote (U+0027) or double quote (U+0022). 1296 | if (ch === 0x27 || ch === 0x22) { 1297 | return scanStringLiteral(); 1298 | } 1299 | 1300 | 1301 | // Dot (.) U+002E can also start a floating-point number, hence the need 1302 | // to check the next character. 1303 | if (ch === 0x2E) { 1304 | if (isDecimalDigit(source.charCodeAt(index + 1))) { 1305 | return scanNumericLiteral(); 1306 | } 1307 | return scanPunctuator(); 1308 | } 1309 | 1310 | if (isDecimalDigit(ch)) { 1311 | return scanNumericLiteral(); 1312 | } 1313 | 1314 | // Slash (/) U+002F can also start a regex. 1315 | if (extra.tokenize && ch === 0x2F) { 1316 | return advanceSlash(); 1317 | } 1318 | 1319 | return scanPunctuator(); 1320 | } 1321 | 1322 | function collectToken() { 1323 | var loc, token, range, value; 1324 | 1325 | skipComment(); 1326 | loc = { 1327 | start: { 1328 | line: lineNumber, 1329 | column: index - lineStart 1330 | } 1331 | }; 1332 | 1333 | token = advance(); 1334 | loc.end = { 1335 | line: lineNumber, 1336 | column: index - lineStart 1337 | }; 1338 | 1339 | if (token.type !== Token.EOF) { 1340 | value = source.slice(token.start, token.end); 1341 | extra.tokens.push({ 1342 | type: TokenName[token.type], 1343 | value: value, 1344 | range: [token.start, token.end], 1345 | loc: loc 1346 | }); 1347 | } 1348 | 1349 | return token; 1350 | } 1351 | 1352 | function lex() { 1353 | var token; 1354 | 1355 | token = lookahead; 1356 | index = token.end; 1357 | lineNumber = token.lineNumber; 1358 | lineStart = token.lineStart; 1359 | 1360 | lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); 1361 | 1362 | index = token.end; 1363 | lineNumber = token.lineNumber; 1364 | lineStart = token.lineStart; 1365 | 1366 | return token; 1367 | } 1368 | 1369 | function peek() { 1370 | var pos, line, start; 1371 | 1372 | pos = index; 1373 | line = lineNumber; 1374 | start = lineStart; 1375 | lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); 1376 | index = pos; 1377 | lineNumber = line; 1378 | lineStart = start; 1379 | } 1380 | 1381 | function Position(line, column) { 1382 | this.line = line; 1383 | this.column = column; 1384 | } 1385 | 1386 | function SourceLocation(startLine, startColumn, line, column) { 1387 | this.start = new Position(startLine, startColumn); 1388 | this.end = new Position(line, column); 1389 | } 1390 | 1391 | SyntaxTreeDelegate = { 1392 | 1393 | name: 'SyntaxTree', 1394 | 1395 | processComment: function (node) { 1396 | var lastChild, trailingComments; 1397 | 1398 | if (node.type === Syntax.Program) { 1399 | if (node.body.length > 0) { 1400 | return; 1401 | } 1402 | } 1403 | 1404 | if (extra.trailingComments.length > 0) { 1405 | if (extra.trailingComments[0].range[0] >= node.range[1]) { 1406 | trailingComments = extra.trailingComments; 1407 | extra.trailingComments = []; 1408 | } else { 1409 | extra.trailingComments.length = 0; 1410 | } 1411 | } else { 1412 | if (extra.bottomRightStack.length > 0 && 1413 | extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments && 1414 | extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) { 1415 | trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments; 1416 | delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments; 1417 | } 1418 | } 1419 | 1420 | // Eating the stack. 1421 | while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) { 1422 | lastChild = extra.bottomRightStack.pop(); 1423 | } 1424 | 1425 | if (lastChild) { 1426 | if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) { 1427 | node.leadingComments = lastChild.leadingComments; 1428 | delete lastChild.leadingComments; 1429 | } 1430 | } else if (extra.leadingComments.length > 0 && extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) { 1431 | node.leadingComments = extra.leadingComments; 1432 | extra.leadingComments = []; 1433 | } 1434 | 1435 | 1436 | if (trailingComments) { 1437 | node.trailingComments = trailingComments; 1438 | } 1439 | 1440 | extra.bottomRightStack.push(node); 1441 | }, 1442 | 1443 | markEnd: function (node, startToken) { 1444 | if (extra.range) { 1445 | node.range = [startToken.start, index]; 1446 | } 1447 | if (extra.loc) { 1448 | node.loc = new SourceLocation( 1449 | startToken.startLineNumber === undefined ? startToken.lineNumber : startToken.startLineNumber, 1450 | startToken.start - (startToken.startLineStart === undefined ? startToken.lineStart : startToken.startLineStart), 1451 | lineNumber, 1452 | index - lineStart 1453 | ); 1454 | this.postProcess(node); 1455 | } 1456 | 1457 | if (extra.attachComment) { 1458 | this.processComment(node); 1459 | } 1460 | return node; 1461 | }, 1462 | 1463 | postProcess: function (node) { 1464 | if (extra.source) { 1465 | node.loc.source = extra.source; 1466 | } 1467 | return node; 1468 | }, 1469 | 1470 | createArrayExpression: function (elements) { 1471 | return { 1472 | type: Syntax.ArrayExpression, 1473 | elements: elements 1474 | }; 1475 | }, 1476 | 1477 | createAssignmentExpression: function (operator, left, right) { 1478 | return { 1479 | type: Syntax.AssignmentExpression, 1480 | operator: operator, 1481 | left: left, 1482 | right: right 1483 | }; 1484 | }, 1485 | 1486 | createBinaryExpression: function (operator, left, right) { 1487 | var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : 1488 | Syntax.BinaryExpression; 1489 | return { 1490 | type: type, 1491 | operator: operator, 1492 | left: left, 1493 | right: right 1494 | }; 1495 | }, 1496 | 1497 | createBlockStatement: function (body) { 1498 | return { 1499 | type: Syntax.BlockStatement, 1500 | body: body 1501 | }; 1502 | }, 1503 | 1504 | createBreakStatement: function (label) { 1505 | return { 1506 | type: Syntax.BreakStatement, 1507 | label: label 1508 | }; 1509 | }, 1510 | 1511 | createCallExpression: function (callee, args) { 1512 | return { 1513 | type: Syntax.CallExpression, 1514 | callee: callee, 1515 | 'arguments': args 1516 | }; 1517 | }, 1518 | 1519 | createCatchClause: function (param, body) { 1520 | return { 1521 | type: Syntax.CatchClause, 1522 | param: param, 1523 | body: body 1524 | }; 1525 | }, 1526 | 1527 | createConditionalExpression: function (test, consequent, alternate) { 1528 | return { 1529 | type: Syntax.ConditionalExpression, 1530 | test: test, 1531 | consequent: consequent, 1532 | alternate: alternate 1533 | }; 1534 | }, 1535 | 1536 | createContinueStatement: function (label) { 1537 | return { 1538 | type: Syntax.ContinueStatement, 1539 | label: label 1540 | }; 1541 | }, 1542 | 1543 | createDebuggerStatement: function () { 1544 | return { 1545 | type: Syntax.DebuggerStatement 1546 | }; 1547 | }, 1548 | 1549 | createDoWhileStatement: function (body, test) { 1550 | return { 1551 | type: Syntax.DoWhileStatement, 1552 | body: body, 1553 | test: test 1554 | }; 1555 | }, 1556 | 1557 | createEmptyStatement: function () { 1558 | return { 1559 | type: Syntax.EmptyStatement 1560 | }; 1561 | }, 1562 | 1563 | createExpressionStatement: function (expression) { 1564 | return { 1565 | type: Syntax.ExpressionStatement, 1566 | expression: expression 1567 | }; 1568 | }, 1569 | 1570 | createForStatement: function (init, test, update, body) { 1571 | return { 1572 | type: Syntax.ForStatement, 1573 | init: init, 1574 | test: test, 1575 | update: update, 1576 | body: body 1577 | }; 1578 | }, 1579 | 1580 | createForInStatement: function (left, right, body) { 1581 | return { 1582 | type: Syntax.ForInStatement, 1583 | left: left, 1584 | right: right, 1585 | body: body, 1586 | each: false 1587 | }; 1588 | }, 1589 | 1590 | createFunctionDeclaration: function (id, params, defaults, body) { 1591 | return { 1592 | type: Syntax.FunctionDeclaration, 1593 | id: id, 1594 | params: params, 1595 | defaults: defaults, 1596 | body: body, 1597 | rest: null, 1598 | generator: false, 1599 | expression: false 1600 | }; 1601 | }, 1602 | 1603 | createFunctionExpression: function (id, params, defaults, body) { 1604 | return { 1605 | type: Syntax.FunctionExpression, 1606 | id: id, 1607 | params: params, 1608 | defaults: defaults, 1609 | body: body, 1610 | rest: null, 1611 | generator: false, 1612 | expression: false 1613 | }; 1614 | }, 1615 | 1616 | createIdentifier: function (name) { 1617 | return { 1618 | type: Syntax.Identifier, 1619 | name: name 1620 | }; 1621 | }, 1622 | 1623 | createIfStatement: function (test, consequent, alternate) { 1624 | return { 1625 | type: Syntax.IfStatement, 1626 | test: test, 1627 | consequent: consequent, 1628 | alternate: alternate 1629 | }; 1630 | }, 1631 | 1632 | createLabeledStatement: function (label, body) { 1633 | return { 1634 | type: Syntax.LabeledStatement, 1635 | label: label, 1636 | body: body 1637 | }; 1638 | }, 1639 | 1640 | createLiteral: function (token) { 1641 | return { 1642 | type: Syntax.Literal, 1643 | value: token.value, 1644 | raw: source.slice(token.start, token.end) 1645 | }; 1646 | }, 1647 | 1648 | createMemberExpression: function (accessor, object, property) { 1649 | return { 1650 | type: Syntax.MemberExpression, 1651 | computed: accessor === '[', 1652 | object: object, 1653 | property: property 1654 | }; 1655 | }, 1656 | 1657 | createNewExpression: function (callee, args) { 1658 | return { 1659 | type: Syntax.NewExpression, 1660 | callee: callee, 1661 | 'arguments': args 1662 | }; 1663 | }, 1664 | 1665 | createObjectExpression: function (properties) { 1666 | return { 1667 | type: Syntax.ObjectExpression, 1668 | properties: properties 1669 | }; 1670 | }, 1671 | 1672 | createPostfixExpression: function (operator, argument) { 1673 | return { 1674 | type: Syntax.UpdateExpression, 1675 | operator: operator, 1676 | argument: argument, 1677 | prefix: false 1678 | }; 1679 | }, 1680 | 1681 | createProgram: function (body) { 1682 | return { 1683 | type: Syntax.Program, 1684 | body: body 1685 | }; 1686 | }, 1687 | 1688 | createProperty: function (kind, key, value) { 1689 | return { 1690 | type: Syntax.Property, 1691 | key: key, 1692 | value: value, 1693 | kind: kind 1694 | }; 1695 | }, 1696 | 1697 | createReturnStatement: function (argument) { 1698 | return { 1699 | type: Syntax.ReturnStatement, 1700 | argument: argument 1701 | }; 1702 | }, 1703 | 1704 | createSequenceExpression: function (expressions) { 1705 | return { 1706 | type: Syntax.SequenceExpression, 1707 | expressions: expressions 1708 | }; 1709 | }, 1710 | 1711 | createSwitchCase: function (test, consequent) { 1712 | return { 1713 | type: Syntax.SwitchCase, 1714 | test: test, 1715 | consequent: consequent 1716 | }; 1717 | }, 1718 | 1719 | createSwitchStatement: function (discriminant, cases) { 1720 | return { 1721 | type: Syntax.SwitchStatement, 1722 | discriminant: discriminant, 1723 | cases: cases 1724 | }; 1725 | }, 1726 | 1727 | createThisExpression: function () { 1728 | return { 1729 | type: Syntax.ThisExpression 1730 | }; 1731 | }, 1732 | 1733 | createThrowStatement: function (argument) { 1734 | return { 1735 | type: Syntax.ThrowStatement, 1736 | argument: argument 1737 | }; 1738 | }, 1739 | 1740 | createTryStatement: function (block, guardedHandlers, handlers, finalizer) { 1741 | return { 1742 | type: Syntax.TryStatement, 1743 | block: block, 1744 | guardedHandlers: guardedHandlers, 1745 | handlers: handlers, 1746 | finalizer: finalizer 1747 | }; 1748 | }, 1749 | 1750 | createUnaryExpression: function (operator, argument) { 1751 | if (operator === '++' || operator === '--') { 1752 | return { 1753 | type: Syntax.UpdateExpression, 1754 | operator: operator, 1755 | argument: argument, 1756 | prefix: true 1757 | }; 1758 | } 1759 | return { 1760 | type: Syntax.UnaryExpression, 1761 | operator: operator, 1762 | argument: argument, 1763 | prefix: true 1764 | }; 1765 | }, 1766 | 1767 | createVariableDeclaration: function (declarations, kind) { 1768 | return { 1769 | type: Syntax.VariableDeclaration, 1770 | declarations: declarations, 1771 | kind: kind 1772 | }; 1773 | }, 1774 | 1775 | createVariableDeclarator: function (id, init) { 1776 | return { 1777 | type: Syntax.VariableDeclarator, 1778 | id: id, 1779 | init: init 1780 | }; 1781 | }, 1782 | 1783 | createWhileStatement: function (test, body) { 1784 | return { 1785 | type: Syntax.WhileStatement, 1786 | test: test, 1787 | body: body 1788 | }; 1789 | }, 1790 | 1791 | createWithStatement: function (object, body) { 1792 | return { 1793 | type: Syntax.WithStatement, 1794 | object: object, 1795 | body: body 1796 | }; 1797 | } 1798 | }; 1799 | 1800 | // Return true if there is a line terminator before the next token. 1801 | 1802 | function peekLineTerminator() { 1803 | var pos, line, start, found; 1804 | 1805 | pos = index; 1806 | line = lineNumber; 1807 | start = lineStart; 1808 | skipComment(); 1809 | found = lineNumber !== line; 1810 | index = pos; 1811 | lineNumber = line; 1812 | lineStart = start; 1813 | 1814 | return found; 1815 | } 1816 | 1817 | // Throw an exception 1818 | 1819 | function throwError(token, messageFormat) { 1820 | var error, 1821 | args = Array.prototype.slice.call(arguments, 2), 1822 | msg = messageFormat.replace( 1823 | /%(\d)/g, 1824 | function (whole, index) { 1825 | assert(index < args.length, 'Message reference must be in range'); 1826 | return args[index]; 1827 | } 1828 | ); 1829 | 1830 | if (typeof token.lineNumber === 'number') { 1831 | error = new Error('Line ' + token.lineNumber + ': ' + msg); 1832 | error.index = token.start; 1833 | error.lineNumber = token.lineNumber; 1834 | error.column = token.start - lineStart + 1; 1835 | } else { 1836 | error = new Error('Line ' + lineNumber + ': ' + msg); 1837 | error.index = index; 1838 | error.lineNumber = lineNumber; 1839 | error.column = index - lineStart + 1; 1840 | } 1841 | 1842 | error.description = msg; 1843 | throw error; 1844 | } 1845 | 1846 | function throwErrorTolerant() { 1847 | try { 1848 | throwError.apply(null, arguments); 1849 | } catch (e) { 1850 | if (extra.errors) { 1851 | extra.errors.push(e); 1852 | } else { 1853 | throw e; 1854 | } 1855 | } 1856 | } 1857 | 1858 | 1859 | // Throw an exception because of the token. 1860 | 1861 | function throwUnexpected(token) { 1862 | if (token.type === Token.EOF) { 1863 | throwError(token, Messages.UnexpectedEOS); 1864 | } 1865 | 1866 | if (token.type === Token.NumericLiteral) { 1867 | throwError(token, Messages.UnexpectedNumber); 1868 | } 1869 | 1870 | if (token.type === Token.StringLiteral) { 1871 | throwError(token, Messages.UnexpectedString); 1872 | } 1873 | 1874 | if (token.type === Token.Identifier) { 1875 | throwError(token, Messages.UnexpectedIdentifier); 1876 | } 1877 | 1878 | if (token.type === Token.Keyword) { 1879 | if (isFutureReservedWord(token.value)) { 1880 | throwError(token, Messages.UnexpectedReserved); 1881 | } else if (strict && isStrictModeReservedWord(token.value)) { 1882 | throwErrorTolerant(token, Messages.StrictReservedWord); 1883 | return; 1884 | } 1885 | throwError(token, Messages.UnexpectedToken, token.value); 1886 | } 1887 | 1888 | // BooleanLiteral, NullLiteral, or Punctuator. 1889 | throwError(token, Messages.UnexpectedToken, token.value); 1890 | } 1891 | 1892 | // Expect the next token to match the specified punctuator. 1893 | // If not, an exception will be thrown. 1894 | 1895 | function expect(value) { 1896 | var token = lex(); 1897 | if (token.type !== Token.Punctuator || token.value !== value) { 1898 | throwUnexpected(token); 1899 | } 1900 | } 1901 | 1902 | // Expect the next token to match the specified keyword. 1903 | // If not, an exception will be thrown. 1904 | 1905 | function expectKeyword(keyword) { 1906 | var token = lex(); 1907 | if (token.type !== Token.Keyword || token.value !== keyword) { 1908 | throwUnexpected(token); 1909 | } 1910 | } 1911 | 1912 | // Return true if the next token matches the specified punctuator. 1913 | 1914 | function match(value) { 1915 | return lookahead.type === Token.Punctuator && lookahead.value === value; 1916 | } 1917 | 1918 | // Return true if the next token matches the specified keyword 1919 | 1920 | function matchKeyword(keyword) { 1921 | return lookahead.type === Token.Keyword && lookahead.value === keyword; 1922 | } 1923 | 1924 | // Return true if the next token is an assignment operator 1925 | 1926 | function matchAssign() { 1927 | var op; 1928 | 1929 | if (lookahead.type !== Token.Punctuator) { 1930 | return false; 1931 | } 1932 | op = lookahead.value; 1933 | return op === '=' || 1934 | op === '*=' || 1935 | op === '/=' || 1936 | op === '%=' || 1937 | op === '+=' || 1938 | op === '-=' || 1939 | op === '<<=' || 1940 | op === '>>=' || 1941 | op === '>>>=' || 1942 | op === '&=' || 1943 | op === '^=' || 1944 | op === '|='; 1945 | } 1946 | 1947 | function consumeSemicolon() { 1948 | var line; 1949 | 1950 | // Catch the very common case first: immediately a semicolon (U+003B). 1951 | if (source.charCodeAt(index) === 0x3B || match(';')) { 1952 | lex(); 1953 | return; 1954 | } 1955 | 1956 | line = lineNumber; 1957 | skipComment(); 1958 | if (lineNumber !== line) { 1959 | return; 1960 | } 1961 | 1962 | if (lookahead.type !== Token.EOF && !match('}')) { 1963 | throwUnexpected(lookahead); 1964 | } 1965 | } 1966 | 1967 | // Return true if provided expression is LeftHandSideExpression 1968 | 1969 | function isLeftHandSide(expr) { 1970 | return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression; 1971 | } 1972 | 1973 | // 11.1.4 Array Initialiser 1974 | 1975 | function parseArrayInitialiser() { 1976 | var elements = [], startToken; 1977 | 1978 | startToken = lookahead; 1979 | expect('['); 1980 | 1981 | while (!match(']')) { 1982 | if (match(',')) { 1983 | lex(); 1984 | elements.push(null); 1985 | } else { 1986 | elements.push(parseAssignmentExpression()); 1987 | 1988 | if (!match(']')) { 1989 | expect(','); 1990 | } 1991 | } 1992 | } 1993 | 1994 | lex(); 1995 | 1996 | return delegate.markEnd(delegate.createArrayExpression(elements), startToken); 1997 | } 1998 | 1999 | // 11.1.5 Object Initialiser 2000 | 2001 | function parsePropertyFunction(param, first) { 2002 | var previousStrict, body, startToken; 2003 | 2004 | previousStrict = strict; 2005 | startToken = lookahead; 2006 | body = parseFunctionSourceElements(); 2007 | if (first && strict && isRestrictedWord(param[0].name)) { 2008 | throwErrorTolerant(first, Messages.StrictParamName); 2009 | } 2010 | strict = previousStrict; 2011 | return delegate.markEnd(delegate.createFunctionExpression(null, param, [], body), startToken); 2012 | } 2013 | 2014 | function parseObjectPropertyKey() { 2015 | var token, startToken; 2016 | 2017 | startToken = lookahead; 2018 | token = lex(); 2019 | 2020 | // Note: This function is called only from parseObjectProperty(), where 2021 | // EOF and Punctuator tokens are already filtered out. 2022 | 2023 | if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { 2024 | if (strict && token.octal) { 2025 | throwErrorTolerant(token, Messages.StrictOctalLiteral); 2026 | } 2027 | return delegate.markEnd(delegate.createLiteral(token), startToken); 2028 | } 2029 | 2030 | return delegate.markEnd(delegate.createIdentifier(token.value), startToken); 2031 | } 2032 | 2033 | function parseObjectProperty() { 2034 | var token, key, id, value, param, startToken; 2035 | 2036 | token = lookahead; 2037 | startToken = lookahead; 2038 | 2039 | if (token.type === Token.Identifier) { 2040 | 2041 | id = parseObjectPropertyKey(); 2042 | 2043 | // Property Assignment: Getter and Setter. 2044 | 2045 | if (token.value === 'get' && !match(':')) { 2046 | key = parseObjectPropertyKey(); 2047 | expect('('); 2048 | expect(')'); 2049 | value = parsePropertyFunction([]); 2050 | return delegate.markEnd(delegate.createProperty('get', key, value), startToken); 2051 | } 2052 | if (token.value === 'set' && !match(':')) { 2053 | key = parseObjectPropertyKey(); 2054 | expect('('); 2055 | token = lookahead; 2056 | if (token.type !== Token.Identifier) { 2057 | expect(')'); 2058 | throwErrorTolerant(token, Messages.UnexpectedToken, token.value); 2059 | value = parsePropertyFunction([]); 2060 | } else { 2061 | param = [ parseVariableIdentifier() ]; 2062 | expect(')'); 2063 | value = parsePropertyFunction(param, token); 2064 | } 2065 | return delegate.markEnd(delegate.createProperty('set', key, value), startToken); 2066 | } 2067 | expect(':'); 2068 | value = parseAssignmentExpression(); 2069 | return delegate.markEnd(delegate.createProperty('init', id, value), startToken); 2070 | } 2071 | if (token.type === Token.EOF || token.type === Token.Punctuator) { 2072 | throwUnexpected(token); 2073 | } else { 2074 | key = parseObjectPropertyKey(); 2075 | expect(':'); 2076 | value = parseAssignmentExpression(); 2077 | return delegate.markEnd(delegate.createProperty('init', key, value), startToken); 2078 | } 2079 | } 2080 | 2081 | function parseObjectInitialiser() { 2082 | var properties = [], property, name, key, kind, map = {}, toString = String, startToken; 2083 | 2084 | startToken = lookahead; 2085 | 2086 | expect('{'); 2087 | 2088 | while (!match('}')) { 2089 | property = parseObjectProperty(); 2090 | 2091 | if (property.key.type === Syntax.Identifier) { 2092 | name = property.key.name; 2093 | } else { 2094 | name = toString(property.key.value); 2095 | } 2096 | kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; 2097 | 2098 | key = '$' + name; 2099 | if (Object.prototype.hasOwnProperty.call(map, key)) { 2100 | if (map[key] === PropertyKind.Data) { 2101 | if (strict && kind === PropertyKind.Data) { 2102 | throwErrorTolerant({}, Messages.StrictDuplicateProperty); 2103 | } else if (kind !== PropertyKind.Data) { 2104 | throwErrorTolerant({}, Messages.AccessorDataProperty); 2105 | } 2106 | } else { 2107 | if (kind === PropertyKind.Data) { 2108 | throwErrorTolerant({}, Messages.AccessorDataProperty); 2109 | } else if (map[key] & kind) { 2110 | throwErrorTolerant({}, Messages.AccessorGetSet); 2111 | } 2112 | } 2113 | map[key] |= kind; 2114 | } else { 2115 | map[key] = kind; 2116 | } 2117 | 2118 | properties.push(property); 2119 | 2120 | if (!match('}')) { 2121 | expect(','); 2122 | } 2123 | } 2124 | 2125 | expect('}'); 2126 | 2127 | return delegate.markEnd(delegate.createObjectExpression(properties), startToken); 2128 | } 2129 | 2130 | // 11.1.6 The Grouping Operator 2131 | 2132 | function parseGroupExpression() { 2133 | var expr; 2134 | 2135 | expect('('); 2136 | 2137 | expr = parseExpression(); 2138 | 2139 | expect(')'); 2140 | 2141 | return expr; 2142 | } 2143 | 2144 | 2145 | // 11.1 Primary Expressions 2146 | 2147 | function parsePrimaryExpression() { 2148 | var type, token, expr, startToken; 2149 | 2150 | if (match('(')) { 2151 | return parseGroupExpression(); 2152 | } 2153 | 2154 | if (match('[')) { 2155 | return parseArrayInitialiser(); 2156 | } 2157 | 2158 | if (match('{')) { 2159 | return parseObjectInitialiser(); 2160 | } 2161 | 2162 | type = lookahead.type; 2163 | startToken = lookahead; 2164 | 2165 | if (type === Token.Identifier) { 2166 | expr = delegate.createIdentifier(lex().value); 2167 | } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { 2168 | if (strict && lookahead.octal) { 2169 | throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); 2170 | } 2171 | expr = delegate.createLiteral(lex()); 2172 | } else if (type === Token.Keyword) { 2173 | if (matchKeyword('function')) { 2174 | return parseFunctionExpression(); 2175 | } 2176 | if (matchKeyword('this')) { 2177 | lex(); 2178 | expr = delegate.createThisExpression(); 2179 | } else { 2180 | throwUnexpected(lex()); 2181 | } 2182 | } else if (type === Token.BooleanLiteral) { 2183 | token = lex(); 2184 | token.value = (token.value === 'true'); 2185 | expr = delegate.createLiteral(token); 2186 | } else if (type === Token.NullLiteral) { 2187 | token = lex(); 2188 | token.value = null; 2189 | expr = delegate.createLiteral(token); 2190 | } else if (match('/') || match('/=')) { 2191 | if (typeof extra.tokens !== 'undefined') { 2192 | expr = delegate.createLiteral(collectRegex()); 2193 | } else { 2194 | expr = delegate.createLiteral(scanRegExp()); 2195 | } 2196 | peek(); 2197 | } else { 2198 | throwUnexpected(lex()); 2199 | } 2200 | 2201 | return delegate.markEnd(expr, startToken); 2202 | } 2203 | 2204 | // 11.2 Left-Hand-Side Expressions 2205 | 2206 | function parseArguments() { 2207 | var args = []; 2208 | 2209 | expect('('); 2210 | 2211 | if (!match(')')) { 2212 | while (index < length) { 2213 | args.push(parseAssignmentExpression()); 2214 | if (match(')')) { 2215 | break; 2216 | } 2217 | expect(','); 2218 | } 2219 | } 2220 | 2221 | expect(')'); 2222 | 2223 | return args; 2224 | } 2225 | 2226 | function parseNonComputedProperty() { 2227 | var token, startToken; 2228 | 2229 | startToken = lookahead; 2230 | token = lex(); 2231 | 2232 | if (!isIdentifierName(token)) { 2233 | throwUnexpected(token); 2234 | } 2235 | 2236 | return delegate.markEnd(delegate.createIdentifier(token.value), startToken); 2237 | } 2238 | 2239 | function parseNonComputedMember() { 2240 | expect('.'); 2241 | 2242 | return parseNonComputedProperty(); 2243 | } 2244 | 2245 | function parseComputedMember() { 2246 | var expr; 2247 | 2248 | expect('['); 2249 | 2250 | expr = parseExpression(); 2251 | 2252 | expect(']'); 2253 | 2254 | return expr; 2255 | } 2256 | 2257 | function parseNewExpression() { 2258 | var callee, args, startToken; 2259 | 2260 | startToken = lookahead; 2261 | expectKeyword('new'); 2262 | callee = parseLeftHandSideExpression(); 2263 | args = match('(') ? parseArguments() : []; 2264 | 2265 | return delegate.markEnd(delegate.createNewExpression(callee, args), startToken); 2266 | } 2267 | 2268 | function parseLeftHandSideExpressionAllowCall() { 2269 | var previousAllowIn, expr, args, property, startToken; 2270 | 2271 | startToken = lookahead; 2272 | 2273 | previousAllowIn = state.allowIn; 2274 | state.allowIn = true; 2275 | expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); 2276 | state.allowIn = previousAllowIn; 2277 | 2278 | for (;;) { 2279 | if (match('.')) { 2280 | property = parseNonComputedMember(); 2281 | expr = delegate.createMemberExpression('.', expr, property); 2282 | } else if (match('(')) { 2283 | args = parseArguments(); 2284 | expr = delegate.createCallExpression(expr, args); 2285 | } else if (match('[')) { 2286 | property = parseComputedMember(); 2287 | expr = delegate.createMemberExpression('[', expr, property); 2288 | } else { 2289 | break; 2290 | } 2291 | delegate.markEnd(expr, startToken); 2292 | } 2293 | 2294 | return expr; 2295 | } 2296 | 2297 | function parseLeftHandSideExpression() { 2298 | var previousAllowIn, expr, property, startToken; 2299 | 2300 | startToken = lookahead; 2301 | 2302 | previousAllowIn = state.allowIn; 2303 | expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); 2304 | state.allowIn = previousAllowIn; 2305 | 2306 | while (match('.') || match('[')) { 2307 | if (match('[')) { 2308 | property = parseComputedMember(); 2309 | expr = delegate.createMemberExpression('[', expr, property); 2310 | } else { 2311 | property = parseNonComputedMember(); 2312 | expr = delegate.createMemberExpression('.', expr, property); 2313 | } 2314 | delegate.markEnd(expr, startToken); 2315 | } 2316 | 2317 | return expr; 2318 | } 2319 | 2320 | // 11.3 Postfix Expressions 2321 | 2322 | function parsePostfixExpression() { 2323 | var expr, token, startToken = lookahead; 2324 | 2325 | expr = parseLeftHandSideExpressionAllowCall(); 2326 | 2327 | if (lookahead.type === Token.Punctuator) { 2328 | if ((match('++') || match('--')) && !peekLineTerminator()) { 2329 | // 11.3.1, 11.3.2 2330 | if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { 2331 | throwErrorTolerant({}, Messages.StrictLHSPostfix); 2332 | } 2333 | 2334 | if (!isLeftHandSide(expr)) { 2335 | throwErrorTolerant({}, Messages.InvalidLHSInAssignment); 2336 | } 2337 | 2338 | token = lex(); 2339 | expr = delegate.markEnd(delegate.createPostfixExpression(token.value, expr), startToken); 2340 | } 2341 | } 2342 | 2343 | return expr; 2344 | } 2345 | 2346 | // 11.4 Unary Operators 2347 | 2348 | function parseUnaryExpression() { 2349 | var token, expr, startToken; 2350 | 2351 | if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { 2352 | expr = parsePostfixExpression(); 2353 | } else if (match('++') || match('--')) { 2354 | startToken = lookahead; 2355 | token = lex(); 2356 | expr = parseUnaryExpression(); 2357 | // 11.4.4, 11.4.5 2358 | if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { 2359 | throwErrorTolerant({}, Messages.StrictLHSPrefix); 2360 | } 2361 | 2362 | if (!isLeftHandSide(expr)) { 2363 | throwErrorTolerant({}, Messages.InvalidLHSInAssignment); 2364 | } 2365 | 2366 | expr = delegate.createUnaryExpression(token.value, expr); 2367 | expr = delegate.markEnd(expr, startToken); 2368 | } else if (match('+') || match('-') || match('~') || match('!')) { 2369 | startToken = lookahead; 2370 | token = lex(); 2371 | expr = parseUnaryExpression(); 2372 | expr = delegate.createUnaryExpression(token.value, expr); 2373 | expr = delegate.markEnd(expr, startToken); 2374 | } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { 2375 | startToken = lookahead; 2376 | token = lex(); 2377 | expr = parseUnaryExpression(); 2378 | expr = delegate.createUnaryExpression(token.value, expr); 2379 | expr = delegate.markEnd(expr, startToken); 2380 | if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { 2381 | throwErrorTolerant({}, Messages.StrictDelete); 2382 | } 2383 | } else { 2384 | expr = parsePostfixExpression(); 2385 | } 2386 | 2387 | return expr; 2388 | } 2389 | 2390 | function binaryPrecedence(token, allowIn) { 2391 | var prec = 0; 2392 | 2393 | if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { 2394 | return 0; 2395 | } 2396 | 2397 | switch (token.value) { 2398 | case '||': 2399 | prec = 1; 2400 | break; 2401 | 2402 | case '&&': 2403 | prec = 2; 2404 | break; 2405 | 2406 | case '|': 2407 | prec = 3; 2408 | break; 2409 | 2410 | case '^': 2411 | prec = 4; 2412 | break; 2413 | 2414 | case '&': 2415 | prec = 5; 2416 | break; 2417 | 2418 | case '==': 2419 | case '!=': 2420 | case '===': 2421 | case '!==': 2422 | prec = 6; 2423 | break; 2424 | 2425 | case '<': 2426 | case '>': 2427 | case '<=': 2428 | case '>=': 2429 | case 'instanceof': 2430 | prec = 7; 2431 | break; 2432 | 2433 | case 'in': 2434 | prec = allowIn ? 7 : 0; 2435 | break; 2436 | 2437 | case '<<': 2438 | case '>>': 2439 | case '>>>': 2440 | prec = 8; 2441 | break; 2442 | 2443 | case '+': 2444 | case '-': 2445 | prec = 9; 2446 | break; 2447 | 2448 | case '*': 2449 | case '/': 2450 | case '%': 2451 | prec = 11; 2452 | break; 2453 | 2454 | default: 2455 | break; 2456 | } 2457 | 2458 | return prec; 2459 | } 2460 | 2461 | // 11.5 Multiplicative Operators 2462 | // 11.6 Additive Operators 2463 | // 11.7 Bitwise Shift Operators 2464 | // 11.8 Relational Operators 2465 | // 11.9 Equality Operators 2466 | // 11.10 Binary Bitwise Operators 2467 | // 11.11 Binary Logical Operators 2468 | 2469 | function parseBinaryExpression() { 2470 | var marker, markers, expr, token, prec, stack, right, operator, left, i; 2471 | 2472 | marker = lookahead; 2473 | left = parseUnaryExpression(); 2474 | 2475 | token = lookahead; 2476 | prec = binaryPrecedence(token, state.allowIn); 2477 | if (prec === 0) { 2478 | return left; 2479 | } 2480 | token.prec = prec; 2481 | lex(); 2482 | 2483 | markers = [marker, lookahead]; 2484 | right = parseUnaryExpression(); 2485 | 2486 | stack = [left, token, right]; 2487 | 2488 | while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) { 2489 | 2490 | // Reduce: make a binary expression from the three topmost entries. 2491 | while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { 2492 | right = stack.pop(); 2493 | operator = stack.pop().value; 2494 | left = stack.pop(); 2495 | expr = delegate.createBinaryExpression(operator, left, right); 2496 | markers.pop(); 2497 | marker = markers[markers.length - 1]; 2498 | delegate.markEnd(expr, marker); 2499 | stack.push(expr); 2500 | } 2501 | 2502 | // Shift. 2503 | token = lex(); 2504 | token.prec = prec; 2505 | stack.push(token); 2506 | markers.push(lookahead); 2507 | expr = parseUnaryExpression(); 2508 | stack.push(expr); 2509 | } 2510 | 2511 | // Final reduce to clean-up the stack. 2512 | i = stack.length - 1; 2513 | expr = stack[i]; 2514 | markers.pop(); 2515 | while (i > 1) { 2516 | expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); 2517 | i -= 2; 2518 | marker = markers.pop(); 2519 | delegate.markEnd(expr, marker); 2520 | } 2521 | 2522 | return expr; 2523 | } 2524 | 2525 | 2526 | // 11.12 Conditional Operator 2527 | 2528 | function parseConditionalExpression() { 2529 | var expr, previousAllowIn, consequent, alternate, startToken; 2530 | 2531 | startToken = lookahead; 2532 | 2533 | expr = parseBinaryExpression(); 2534 | 2535 | if (match('?')) { 2536 | lex(); 2537 | previousAllowIn = state.allowIn; 2538 | state.allowIn = true; 2539 | consequent = parseAssignmentExpression(); 2540 | state.allowIn = previousAllowIn; 2541 | expect(':'); 2542 | alternate = parseAssignmentExpression(); 2543 | 2544 | expr = delegate.createConditionalExpression(expr, consequent, alternate); 2545 | delegate.markEnd(expr, startToken); 2546 | } 2547 | 2548 | return expr; 2549 | } 2550 | 2551 | // 11.13 Assignment Operators 2552 | 2553 | function parseAssignmentExpression() { 2554 | var token, left, right, node, startToken; 2555 | 2556 | token = lookahead; 2557 | startToken = lookahead; 2558 | 2559 | node = left = parseConditionalExpression(); 2560 | 2561 | if (matchAssign()) { 2562 | // LeftHandSideExpression 2563 | if (!isLeftHandSide(left)) { 2564 | throwErrorTolerant({}, Messages.InvalidLHSInAssignment); 2565 | } 2566 | 2567 | // 11.13.1 2568 | if (strict && left.type === Syntax.Identifier && isRestrictedWord(left.name)) { 2569 | throwErrorTolerant(token, Messages.StrictLHSAssignment); 2570 | } 2571 | 2572 | token = lex(); 2573 | right = parseAssignmentExpression(); 2574 | node = delegate.markEnd(delegate.createAssignmentExpression(token.value, left, right), startToken); 2575 | } 2576 | 2577 | return node; 2578 | } 2579 | 2580 | // 11.14 Comma Operator 2581 | 2582 | function parseExpression() { 2583 | var expr, startToken = lookahead; 2584 | 2585 | expr = parseAssignmentExpression(); 2586 | 2587 | if (match(',')) { 2588 | expr = delegate.createSequenceExpression([ expr ]); 2589 | 2590 | while (index < length) { 2591 | if (!match(',')) { 2592 | break; 2593 | } 2594 | lex(); 2595 | expr.expressions.push(parseAssignmentExpression()); 2596 | } 2597 | 2598 | delegate.markEnd(expr, startToken); 2599 | } 2600 | 2601 | return expr; 2602 | } 2603 | 2604 | // 12.1 Block 2605 | 2606 | function parseStatementList() { 2607 | var list = [], 2608 | statement; 2609 | 2610 | while (index < length) { 2611 | if (match('}')) { 2612 | break; 2613 | } 2614 | statement = parseSourceElement(); 2615 | if (typeof statement === 'undefined') { 2616 | break; 2617 | } 2618 | list.push(statement); 2619 | } 2620 | 2621 | return list; 2622 | } 2623 | 2624 | function parseBlock() { 2625 | var block, startToken; 2626 | 2627 | startToken = lookahead; 2628 | expect('{'); 2629 | 2630 | block = parseStatementList(); 2631 | 2632 | expect('}'); 2633 | 2634 | return delegate.markEnd(delegate.createBlockStatement(block), startToken); 2635 | } 2636 | 2637 | // 12.2 Variable Statement 2638 | 2639 | function parseVariableIdentifier() { 2640 | var token, startToken; 2641 | 2642 | startToken = lookahead; 2643 | token = lex(); 2644 | 2645 | if (token.type !== Token.Identifier) { 2646 | throwUnexpected(token); 2647 | } 2648 | 2649 | return delegate.markEnd(delegate.createIdentifier(token.value), startToken); 2650 | } 2651 | 2652 | function parseVariableDeclaration(kind) { 2653 | var init = null, id, startToken; 2654 | 2655 | startToken = lookahead; 2656 | id = parseVariableIdentifier(); 2657 | 2658 | // 12.2.1 2659 | if (strict && isRestrictedWord(id.name)) { 2660 | throwErrorTolerant({}, Messages.StrictVarName); 2661 | } 2662 | 2663 | if (kind === 'const') { 2664 | expect('='); 2665 | init = parseAssignmentExpression(); 2666 | } else if (match('=')) { 2667 | lex(); 2668 | init = parseAssignmentExpression(); 2669 | } 2670 | 2671 | return delegate.markEnd(delegate.createVariableDeclarator(id, init), startToken); 2672 | } 2673 | 2674 | function parseVariableDeclarationList(kind) { 2675 | var list = []; 2676 | 2677 | do { 2678 | list.push(parseVariableDeclaration(kind)); 2679 | if (!match(',')) { 2680 | break; 2681 | } 2682 | lex(); 2683 | } while (index < length); 2684 | 2685 | return list; 2686 | } 2687 | 2688 | function parseVariableStatement() { 2689 | var declarations; 2690 | 2691 | expectKeyword('var'); 2692 | 2693 | declarations = parseVariableDeclarationList(); 2694 | 2695 | consumeSemicolon(); 2696 | 2697 | return delegate.createVariableDeclaration(declarations, 'var'); 2698 | } 2699 | 2700 | // kind may be `const` or `let` 2701 | // Both are experimental and not in the specification yet. 2702 | // see http://wiki.ecmascript.org/doku.php?id=harmony:const 2703 | // and http://wiki.ecmascript.org/doku.php?id=harmony:let 2704 | function parseConstLetDeclaration(kind) { 2705 | var declarations, startToken; 2706 | 2707 | startToken = lookahead; 2708 | 2709 | expectKeyword(kind); 2710 | 2711 | declarations = parseVariableDeclarationList(kind); 2712 | 2713 | consumeSemicolon(); 2714 | 2715 | return delegate.markEnd(delegate.createVariableDeclaration(declarations, kind), startToken); 2716 | } 2717 | 2718 | // 12.3 Empty Statement 2719 | 2720 | function parseEmptyStatement() { 2721 | expect(';'); 2722 | return delegate.createEmptyStatement(); 2723 | } 2724 | 2725 | // 12.4 Expression Statement 2726 | 2727 | function parseExpressionStatement() { 2728 | var expr = parseExpression(); 2729 | consumeSemicolon(); 2730 | return delegate.createExpressionStatement(expr); 2731 | } 2732 | 2733 | // 12.5 If statement 2734 | 2735 | function parseIfStatement() { 2736 | var test, consequent, alternate; 2737 | 2738 | expectKeyword('if'); 2739 | 2740 | expect('('); 2741 | 2742 | test = parseExpression(); 2743 | 2744 | expect(')'); 2745 | 2746 | consequent = parseStatement(); 2747 | 2748 | if (matchKeyword('else')) { 2749 | lex(); 2750 | alternate = parseStatement(); 2751 | } else { 2752 | alternate = null; 2753 | } 2754 | 2755 | return delegate.createIfStatement(test, consequent, alternate); 2756 | } 2757 | 2758 | // 12.6 Iteration Statements 2759 | 2760 | function parseDoWhileStatement() { 2761 | var body, test, oldInIteration; 2762 | 2763 | expectKeyword('do'); 2764 | 2765 | oldInIteration = state.inIteration; 2766 | state.inIteration = true; 2767 | 2768 | body = parseStatement(); 2769 | 2770 | state.inIteration = oldInIteration; 2771 | 2772 | expectKeyword('while'); 2773 | 2774 | expect('('); 2775 | 2776 | test = parseExpression(); 2777 | 2778 | expect(')'); 2779 | 2780 | if (match(';')) { 2781 | lex(); 2782 | } 2783 | 2784 | return delegate.createDoWhileStatement(body, test); 2785 | } 2786 | 2787 | function parseWhileStatement() { 2788 | var test, body, oldInIteration; 2789 | 2790 | expectKeyword('while'); 2791 | 2792 | expect('('); 2793 | 2794 | test = parseExpression(); 2795 | 2796 | expect(')'); 2797 | 2798 | oldInIteration = state.inIteration; 2799 | state.inIteration = true; 2800 | 2801 | body = parseStatement(); 2802 | 2803 | state.inIteration = oldInIteration; 2804 | 2805 | return delegate.createWhileStatement(test, body); 2806 | } 2807 | 2808 | function parseForVariableDeclaration() { 2809 | var token, declarations, startToken; 2810 | 2811 | startToken = lookahead; 2812 | token = lex(); 2813 | declarations = parseVariableDeclarationList(); 2814 | 2815 | return delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value), startToken); 2816 | } 2817 | 2818 | function parseForStatement() { 2819 | var init, test, update, left, right, body, oldInIteration; 2820 | 2821 | init = test = update = null; 2822 | 2823 | expectKeyword('for'); 2824 | 2825 | expect('('); 2826 | 2827 | if (match(';')) { 2828 | lex(); 2829 | } else { 2830 | if (matchKeyword('var') || matchKeyword('let')) { 2831 | state.allowIn = false; 2832 | init = parseForVariableDeclaration(); 2833 | state.allowIn = true; 2834 | 2835 | if (init.declarations.length === 1 && matchKeyword('in')) { 2836 | lex(); 2837 | left = init; 2838 | right = parseExpression(); 2839 | init = null; 2840 | } 2841 | } else { 2842 | state.allowIn = false; 2843 | init = parseExpression(); 2844 | state.allowIn = true; 2845 | 2846 | if (matchKeyword('in')) { 2847 | // LeftHandSideExpression 2848 | if (!isLeftHandSide(init)) { 2849 | throwErrorTolerant({}, Messages.InvalidLHSInForIn); 2850 | } 2851 | 2852 | lex(); 2853 | left = init; 2854 | right = parseExpression(); 2855 | init = null; 2856 | } 2857 | } 2858 | 2859 | if (typeof left === 'undefined') { 2860 | expect(';'); 2861 | } 2862 | } 2863 | 2864 | if (typeof left === 'undefined') { 2865 | 2866 | if (!match(';')) { 2867 | test = parseExpression(); 2868 | } 2869 | expect(';'); 2870 | 2871 | if (!match(')')) { 2872 | update = parseExpression(); 2873 | } 2874 | } 2875 | 2876 | expect(')'); 2877 | 2878 | oldInIteration = state.inIteration; 2879 | state.inIteration = true; 2880 | 2881 | body = parseStatement(); 2882 | 2883 | state.inIteration = oldInIteration; 2884 | 2885 | return (typeof left === 'undefined') ? 2886 | delegate.createForStatement(init, test, update, body) : 2887 | delegate.createForInStatement(left, right, body); 2888 | } 2889 | 2890 | // 12.7 The continue statement 2891 | 2892 | function parseContinueStatement() { 2893 | var label = null, key; 2894 | 2895 | expectKeyword('continue'); 2896 | 2897 | // Optimize the most common form: 'continue;'. 2898 | if (source.charCodeAt(index) === 0x3B) { 2899 | lex(); 2900 | 2901 | if (!state.inIteration) { 2902 | throwError({}, Messages.IllegalContinue); 2903 | } 2904 | 2905 | return delegate.createContinueStatement(null); 2906 | } 2907 | 2908 | if (peekLineTerminator()) { 2909 | if (!state.inIteration) { 2910 | throwError({}, Messages.IllegalContinue); 2911 | } 2912 | 2913 | return delegate.createContinueStatement(null); 2914 | } 2915 | 2916 | if (lookahead.type === Token.Identifier) { 2917 | label = parseVariableIdentifier(); 2918 | 2919 | key = '$' + label.name; 2920 | if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { 2921 | throwError({}, Messages.UnknownLabel, label.name); 2922 | } 2923 | } 2924 | 2925 | consumeSemicolon(); 2926 | 2927 | if (label === null && !state.inIteration) { 2928 | throwError({}, Messages.IllegalContinue); 2929 | } 2930 | 2931 | return delegate.createContinueStatement(label); 2932 | } 2933 | 2934 | // 12.8 The break statement 2935 | 2936 | function parseBreakStatement() { 2937 | var label = null, key; 2938 | 2939 | expectKeyword('break'); 2940 | 2941 | // Catch the very common case first: immediately a semicolon (U+003B). 2942 | if (source.charCodeAt(index) === 0x3B) { 2943 | lex(); 2944 | 2945 | if (!(state.inIteration || state.inSwitch)) { 2946 | throwError({}, Messages.IllegalBreak); 2947 | } 2948 | 2949 | return delegate.createBreakStatement(null); 2950 | } 2951 | 2952 | if (peekLineTerminator()) { 2953 | if (!(state.inIteration || state.inSwitch)) { 2954 | throwError({}, Messages.IllegalBreak); 2955 | } 2956 | 2957 | return delegate.createBreakStatement(null); 2958 | } 2959 | 2960 | if (lookahead.type === Token.Identifier) { 2961 | label = parseVariableIdentifier(); 2962 | 2963 | key = '$' + label.name; 2964 | if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { 2965 | throwError({}, Messages.UnknownLabel, label.name); 2966 | } 2967 | } 2968 | 2969 | consumeSemicolon(); 2970 | 2971 | if (label === null && !(state.inIteration || state.inSwitch)) { 2972 | throwError({}, Messages.IllegalBreak); 2973 | } 2974 | 2975 | return delegate.createBreakStatement(label); 2976 | } 2977 | 2978 | // 12.9 The return statement 2979 | 2980 | function parseReturnStatement() { 2981 | var argument = null; 2982 | 2983 | expectKeyword('return'); 2984 | 2985 | if (!state.inFunctionBody) { 2986 | throwErrorTolerant({}, Messages.IllegalReturn); 2987 | } 2988 | 2989 | // 'return' followed by a space and an identifier is very common. 2990 | if (source.charCodeAt(index) === 0x20) { 2991 | if (isIdentifierStart(source.charCodeAt(index + 1))) { 2992 | argument = parseExpression(); 2993 | consumeSemicolon(); 2994 | return delegate.createReturnStatement(argument); 2995 | } 2996 | } 2997 | 2998 | if (peekLineTerminator()) { 2999 | return delegate.createReturnStatement(null); 3000 | } 3001 | 3002 | if (!match(';')) { 3003 | if (!match('}') && lookahead.type !== Token.EOF) { 3004 | argument = parseExpression(); 3005 | } 3006 | } 3007 | 3008 | consumeSemicolon(); 3009 | 3010 | return delegate.createReturnStatement(argument); 3011 | } 3012 | 3013 | // 12.10 The with statement 3014 | 3015 | function parseWithStatement() { 3016 | var object, body; 3017 | 3018 | if (strict) { 3019 | // TODO(ikarienator): Should we update the test cases instead? 3020 | skipComment(); 3021 | throwErrorTolerant({}, Messages.StrictModeWith); 3022 | } 3023 | 3024 | expectKeyword('with'); 3025 | 3026 | expect('('); 3027 | 3028 | object = parseExpression(); 3029 | 3030 | expect(')'); 3031 | 3032 | body = parseStatement(); 3033 | 3034 | return delegate.createWithStatement(object, body); 3035 | } 3036 | 3037 | // 12.10 The swith statement 3038 | 3039 | function parseSwitchCase() { 3040 | var test, consequent = [], statement, startToken; 3041 | 3042 | startToken = lookahead; 3043 | if (matchKeyword('default')) { 3044 | lex(); 3045 | test = null; 3046 | } else { 3047 | expectKeyword('case'); 3048 | test = parseExpression(); 3049 | } 3050 | expect(':'); 3051 | 3052 | while (index < length) { 3053 | if (match('}') || matchKeyword('default') || matchKeyword('case')) { 3054 | break; 3055 | } 3056 | statement = parseStatement(); 3057 | consequent.push(statement); 3058 | } 3059 | 3060 | return delegate.markEnd(delegate.createSwitchCase(test, consequent), startToken); 3061 | } 3062 | 3063 | function parseSwitchStatement() { 3064 | var discriminant, cases, clause, oldInSwitch, defaultFound; 3065 | 3066 | expectKeyword('switch'); 3067 | 3068 | expect('('); 3069 | 3070 | discriminant = parseExpression(); 3071 | 3072 | expect(')'); 3073 | 3074 | expect('{'); 3075 | 3076 | cases = []; 3077 | 3078 | if (match('}')) { 3079 | lex(); 3080 | return delegate.createSwitchStatement(discriminant, cases); 3081 | } 3082 | 3083 | oldInSwitch = state.inSwitch; 3084 | state.inSwitch = true; 3085 | defaultFound = false; 3086 | 3087 | while (index < length) { 3088 | if (match('}')) { 3089 | break; 3090 | } 3091 | clause = parseSwitchCase(); 3092 | if (clause.test === null) { 3093 | if (defaultFound) { 3094 | throwError({}, Messages.MultipleDefaultsInSwitch); 3095 | } 3096 | defaultFound = true; 3097 | } 3098 | cases.push(clause); 3099 | } 3100 | 3101 | state.inSwitch = oldInSwitch; 3102 | 3103 | expect('}'); 3104 | 3105 | return delegate.createSwitchStatement(discriminant, cases); 3106 | } 3107 | 3108 | // 12.13 The throw statement 3109 | 3110 | function parseThrowStatement() { 3111 | var argument; 3112 | 3113 | expectKeyword('throw'); 3114 | 3115 | if (peekLineTerminator()) { 3116 | throwError({}, Messages.NewlineAfterThrow); 3117 | } 3118 | 3119 | argument = parseExpression(); 3120 | 3121 | consumeSemicolon(); 3122 | 3123 | return delegate.createThrowStatement(argument); 3124 | } 3125 | 3126 | // 12.14 The try statement 3127 | 3128 | function parseCatchClause() { 3129 | var param, body, startToken; 3130 | 3131 | startToken = lookahead; 3132 | expectKeyword('catch'); 3133 | 3134 | expect('('); 3135 | if (match(')')) { 3136 | throwUnexpected(lookahead); 3137 | } 3138 | 3139 | param = parseVariableIdentifier(); 3140 | // 12.14.1 3141 | if (strict && isRestrictedWord(param.name)) { 3142 | throwErrorTolerant({}, Messages.StrictCatchVariable); 3143 | } 3144 | 3145 | expect(')'); 3146 | body = parseBlock(); 3147 | return delegate.markEnd(delegate.createCatchClause(param, body), startToken); 3148 | } 3149 | 3150 | function parseTryStatement() { 3151 | var block, handlers = [], finalizer = null; 3152 | 3153 | expectKeyword('try'); 3154 | 3155 | block = parseBlock(); 3156 | 3157 | if (matchKeyword('catch')) { 3158 | handlers.push(parseCatchClause()); 3159 | } 3160 | 3161 | if (matchKeyword('finally')) { 3162 | lex(); 3163 | finalizer = parseBlock(); 3164 | } 3165 | 3166 | if (handlers.length === 0 && !finalizer) { 3167 | throwError({}, Messages.NoCatchOrFinally); 3168 | } 3169 | 3170 | return delegate.createTryStatement(block, [], handlers, finalizer); 3171 | } 3172 | 3173 | // 12.15 The debugger statement 3174 | 3175 | function parseDebuggerStatement() { 3176 | expectKeyword('debugger'); 3177 | 3178 | consumeSemicolon(); 3179 | 3180 | return delegate.createDebuggerStatement(); 3181 | } 3182 | 3183 | // 12 Statements 3184 | 3185 | function parseStatement() { 3186 | var type = lookahead.type, 3187 | expr, 3188 | labeledBody, 3189 | key, 3190 | startToken; 3191 | 3192 | if (type === Token.EOF) { 3193 | throwUnexpected(lookahead); 3194 | } 3195 | 3196 | if (type === Token.Punctuator && lookahead.value === '{') { 3197 | return parseBlock(); 3198 | } 3199 | 3200 | startToken = lookahead; 3201 | 3202 | if (type === Token.Punctuator) { 3203 | switch (lookahead.value) { 3204 | case ';': 3205 | return delegate.markEnd(parseEmptyStatement(), startToken); 3206 | case '(': 3207 | return delegate.markEnd(parseExpressionStatement(), startToken); 3208 | default: 3209 | break; 3210 | } 3211 | } 3212 | 3213 | if (type === Token.Keyword) { 3214 | switch (lookahead.value) { 3215 | case 'break': 3216 | return delegate.markEnd(parseBreakStatement(), startToken); 3217 | case 'continue': 3218 | return delegate.markEnd(parseContinueStatement(), startToken); 3219 | case 'debugger': 3220 | return delegate.markEnd(parseDebuggerStatement(), startToken); 3221 | case 'do': 3222 | return delegate.markEnd(parseDoWhileStatement(), startToken); 3223 | case 'for': 3224 | return delegate.markEnd(parseForStatement(), startToken); 3225 | case 'function': 3226 | return delegate.markEnd(parseFunctionDeclaration(), startToken); 3227 | case 'if': 3228 | return delegate.markEnd(parseIfStatement(), startToken); 3229 | case 'return': 3230 | return delegate.markEnd(parseReturnStatement(), startToken); 3231 | case 'switch': 3232 | return delegate.markEnd(parseSwitchStatement(), startToken); 3233 | case 'throw': 3234 | return delegate.markEnd(parseThrowStatement(), startToken); 3235 | case 'try': 3236 | return delegate.markEnd(parseTryStatement(), startToken); 3237 | case 'var': 3238 | return delegate.markEnd(parseVariableStatement(), startToken); 3239 | case 'while': 3240 | return delegate.markEnd(parseWhileStatement(), startToken); 3241 | case 'with': 3242 | return delegate.markEnd(parseWithStatement(), startToken); 3243 | default: 3244 | break; 3245 | } 3246 | } 3247 | 3248 | expr = parseExpression(); 3249 | 3250 | // 12.12 Labelled Statements 3251 | if ((expr.type === Syntax.Identifier) && match(':')) { 3252 | lex(); 3253 | 3254 | key = '$' + expr.name; 3255 | if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { 3256 | throwError({}, Messages.Redeclaration, 'Label', expr.name); 3257 | } 3258 | 3259 | state.labelSet[key] = true; 3260 | labeledBody = parseStatement(); 3261 | delete state.labelSet[key]; 3262 | return delegate.markEnd(delegate.createLabeledStatement(expr, labeledBody), startToken); 3263 | } 3264 | 3265 | consumeSemicolon(); 3266 | 3267 | return delegate.markEnd(delegate.createExpressionStatement(expr), startToken); 3268 | } 3269 | 3270 | // 13 Function Definition 3271 | 3272 | function parseFunctionSourceElements() { 3273 | var sourceElement, sourceElements = [], token, directive, firstRestricted, 3274 | oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, startToken; 3275 | 3276 | startToken = lookahead; 3277 | expect('{'); 3278 | 3279 | while (index < length) { 3280 | if (lookahead.type !== Token.StringLiteral) { 3281 | break; 3282 | } 3283 | token = lookahead; 3284 | 3285 | sourceElement = parseSourceElement(); 3286 | sourceElements.push(sourceElement); 3287 | if (sourceElement.expression.type !== Syntax.Literal) { 3288 | // this is not directive 3289 | break; 3290 | } 3291 | directive = source.slice(token.start + 1, token.end - 1); 3292 | if (directive === 'use strict') { 3293 | strict = true; 3294 | if (firstRestricted) { 3295 | throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); 3296 | } 3297 | } else { 3298 | if (!firstRestricted && token.octal) { 3299 | firstRestricted = token; 3300 | } 3301 | } 3302 | } 3303 | 3304 | oldLabelSet = state.labelSet; 3305 | oldInIteration = state.inIteration; 3306 | oldInSwitch = state.inSwitch; 3307 | oldInFunctionBody = state.inFunctionBody; 3308 | 3309 | state.labelSet = {}; 3310 | state.inIteration = false; 3311 | state.inSwitch = false; 3312 | state.inFunctionBody = true; 3313 | 3314 | while (index < length) { 3315 | if (match('}')) { 3316 | break; 3317 | } 3318 | sourceElement = parseSourceElement(); 3319 | if (typeof sourceElement === 'undefined') { 3320 | break; 3321 | } 3322 | sourceElements.push(sourceElement); 3323 | } 3324 | 3325 | expect('}'); 3326 | 3327 | state.labelSet = oldLabelSet; 3328 | state.inIteration = oldInIteration; 3329 | state.inSwitch = oldInSwitch; 3330 | state.inFunctionBody = oldInFunctionBody; 3331 | 3332 | return delegate.markEnd(delegate.createBlockStatement(sourceElements), startToken); 3333 | } 3334 | 3335 | function parseParams(firstRestricted) { 3336 | var param, params = [], token, stricted, paramSet, key, message; 3337 | expect('('); 3338 | 3339 | if (!match(')')) { 3340 | paramSet = {}; 3341 | while (index < length) { 3342 | token = lookahead; 3343 | param = parseVariableIdentifier(); 3344 | key = '$' + token.value; 3345 | if (strict) { 3346 | if (isRestrictedWord(token.value)) { 3347 | stricted = token; 3348 | message = Messages.StrictParamName; 3349 | } 3350 | if (Object.prototype.hasOwnProperty.call(paramSet, key)) { 3351 | stricted = token; 3352 | message = Messages.StrictParamDupe; 3353 | } 3354 | } else if (!firstRestricted) { 3355 | if (isRestrictedWord(token.value)) { 3356 | firstRestricted = token; 3357 | message = Messages.StrictParamName; 3358 | } else if (isStrictModeReservedWord(token.value)) { 3359 | firstRestricted = token; 3360 | message = Messages.StrictReservedWord; 3361 | } else if (Object.prototype.hasOwnProperty.call(paramSet, key)) { 3362 | firstRestricted = token; 3363 | message = Messages.StrictParamDupe; 3364 | } 3365 | } 3366 | params.push(param); 3367 | paramSet[key] = true; 3368 | if (match(')')) { 3369 | break; 3370 | } 3371 | expect(','); 3372 | } 3373 | } 3374 | 3375 | expect(')'); 3376 | 3377 | return { 3378 | params: params, 3379 | stricted: stricted, 3380 | firstRestricted: firstRestricted, 3381 | message: message 3382 | }; 3383 | } 3384 | 3385 | function parseFunctionDeclaration() { 3386 | var id, params = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, startToken; 3387 | 3388 | startToken = lookahead; 3389 | 3390 | expectKeyword('function'); 3391 | token = lookahead; 3392 | id = parseVariableIdentifier(); 3393 | if (strict) { 3394 | if (isRestrictedWord(token.value)) { 3395 | throwErrorTolerant(token, Messages.StrictFunctionName); 3396 | } 3397 | } else { 3398 | if (isRestrictedWord(token.value)) { 3399 | firstRestricted = token; 3400 | message = Messages.StrictFunctionName; 3401 | } else if (isStrictModeReservedWord(token.value)) { 3402 | firstRestricted = token; 3403 | message = Messages.StrictReservedWord; 3404 | } 3405 | } 3406 | 3407 | tmp = parseParams(firstRestricted); 3408 | params = tmp.params; 3409 | stricted = tmp.stricted; 3410 | firstRestricted = tmp.firstRestricted; 3411 | if (tmp.message) { 3412 | message = tmp.message; 3413 | } 3414 | 3415 | previousStrict = strict; 3416 | body = parseFunctionSourceElements(); 3417 | if (strict && firstRestricted) { 3418 | throwError(firstRestricted, message); 3419 | } 3420 | if (strict && stricted) { 3421 | throwErrorTolerant(stricted, message); 3422 | } 3423 | strict = previousStrict; 3424 | 3425 | return delegate.markEnd(delegate.createFunctionDeclaration(id, params, [], body), startToken); 3426 | } 3427 | 3428 | function parseFunctionExpression() { 3429 | var token, id = null, stricted, firstRestricted, message, tmp, params = [], body, previousStrict, startToken; 3430 | 3431 | startToken = lookahead; 3432 | expectKeyword('function'); 3433 | 3434 | if (!match('(')) { 3435 | token = lookahead; 3436 | id = parseVariableIdentifier(); 3437 | if (strict) { 3438 | if (isRestrictedWord(token.value)) { 3439 | throwErrorTolerant(token, Messages.StrictFunctionName); 3440 | } 3441 | } else { 3442 | if (isRestrictedWord(token.value)) { 3443 | firstRestricted = token; 3444 | message = Messages.StrictFunctionName; 3445 | } else if (isStrictModeReservedWord(token.value)) { 3446 | firstRestricted = token; 3447 | message = Messages.StrictReservedWord; 3448 | } 3449 | } 3450 | } 3451 | 3452 | tmp = parseParams(firstRestricted); 3453 | params = tmp.params; 3454 | stricted = tmp.stricted; 3455 | firstRestricted = tmp.firstRestricted; 3456 | if (tmp.message) { 3457 | message = tmp.message; 3458 | } 3459 | 3460 | previousStrict = strict; 3461 | body = parseFunctionSourceElements(); 3462 | if (strict && firstRestricted) { 3463 | throwError(firstRestricted, message); 3464 | } 3465 | if (strict && stricted) { 3466 | throwErrorTolerant(stricted, message); 3467 | } 3468 | strict = previousStrict; 3469 | 3470 | return delegate.markEnd(delegate.createFunctionExpression(id, params, [], body), startToken); 3471 | } 3472 | 3473 | // 14 Program 3474 | 3475 | function parseSourceElement() { 3476 | if (lookahead.type === Token.Keyword) { 3477 | switch (lookahead.value) { 3478 | case 'const': 3479 | case 'let': 3480 | return parseConstLetDeclaration(lookahead.value); 3481 | case 'function': 3482 | return parseFunctionDeclaration(); 3483 | default: 3484 | return parseStatement(); 3485 | } 3486 | } 3487 | 3488 | if (lookahead.type !== Token.EOF) { 3489 | return parseStatement(); 3490 | } 3491 | } 3492 | 3493 | function parseSourceElements() { 3494 | var sourceElement, sourceElements = [], token, directive, firstRestricted; 3495 | 3496 | while (index < length) { 3497 | token = lookahead; 3498 | if (token.type !== Token.StringLiteral) { 3499 | break; 3500 | } 3501 | 3502 | sourceElement = parseSourceElement(); 3503 | sourceElements.push(sourceElement); 3504 | if (sourceElement.expression.type !== Syntax.Literal) { 3505 | // this is not directive 3506 | break; 3507 | } 3508 | directive = source.slice(token.start + 1, token.end - 1); 3509 | if (directive === 'use strict') { 3510 | strict = true; 3511 | if (firstRestricted) { 3512 | throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); 3513 | } 3514 | } else { 3515 | if (!firstRestricted && token.octal) { 3516 | firstRestricted = token; 3517 | } 3518 | } 3519 | } 3520 | 3521 | while (index < length) { 3522 | sourceElement = parseSourceElement(); 3523 | /* istanbul ignore if */ 3524 | if (typeof sourceElement === 'undefined') { 3525 | break; 3526 | } 3527 | sourceElements.push(sourceElement); 3528 | } 3529 | return sourceElements; 3530 | } 3531 | 3532 | function parseProgram() { 3533 | var body, startToken; 3534 | 3535 | skipComment(); 3536 | peek(); 3537 | startToken = lookahead; 3538 | strict = false; 3539 | 3540 | body = parseSourceElements(); 3541 | return delegate.markEnd(delegate.createProgram(body), startToken); 3542 | } 3543 | 3544 | function filterTokenLocation() { 3545 | var i, entry, token, tokens = []; 3546 | 3547 | for (i = 0; i < extra.tokens.length; ++i) { 3548 | entry = extra.tokens[i]; 3549 | token = { 3550 | type: entry.type, 3551 | value: entry.value 3552 | }; 3553 | if (extra.range) { 3554 | token.range = entry.range; 3555 | } 3556 | if (extra.loc) { 3557 | token.loc = entry.loc; 3558 | } 3559 | tokens.push(token); 3560 | } 3561 | 3562 | extra.tokens = tokens; 3563 | } 3564 | 3565 | function tokenize(code, options) { 3566 | var toString, 3567 | token, 3568 | tokens; 3569 | 3570 | toString = String; 3571 | if (typeof code !== 'string' && !(code instanceof String)) { 3572 | code = toString(code); 3573 | } 3574 | 3575 | delegate = SyntaxTreeDelegate; 3576 | source = code; 3577 | index = 0; 3578 | lineNumber = (source.length > 0) ? 1 : 0; 3579 | lineStart = 0; 3580 | length = source.length; 3581 | lookahead = null; 3582 | state = { 3583 | allowIn: true, 3584 | labelSet: {}, 3585 | inFunctionBody: false, 3586 | inIteration: false, 3587 | inSwitch: false, 3588 | lastCommentStart: -1 3589 | }; 3590 | 3591 | extra = {}; 3592 | 3593 | // Options matching. 3594 | options = options || {}; 3595 | 3596 | // Of course we collect tokens here. 3597 | options.tokens = true; 3598 | extra.tokens = []; 3599 | extra.tokenize = true; 3600 | // The following two fields are necessary to compute the Regex tokens. 3601 | extra.openParenToken = -1; 3602 | extra.openCurlyToken = -1; 3603 | 3604 | extra.range = (typeof options.range === 'boolean') && options.range; 3605 | extra.loc = (typeof options.loc === 'boolean') && options.loc; 3606 | 3607 | if (typeof options.comment === 'boolean' && options.comment) { 3608 | extra.comments = []; 3609 | } 3610 | if (typeof options.tolerant === 'boolean' && options.tolerant) { 3611 | extra.errors = []; 3612 | } 3613 | 3614 | try { 3615 | peek(); 3616 | if (lookahead.type === Token.EOF) { 3617 | return extra.tokens; 3618 | } 3619 | 3620 | token = lex(); 3621 | while (lookahead.type !== Token.EOF) { 3622 | try { 3623 | token = lex(); 3624 | } catch (lexError) { 3625 | token = lookahead; 3626 | if (extra.errors) { 3627 | extra.errors.push(lexError); 3628 | // We have to break on the first error 3629 | // to avoid infinite loops. 3630 | break; 3631 | } else { 3632 | throw lexError; 3633 | } 3634 | } 3635 | } 3636 | 3637 | filterTokenLocation(); 3638 | tokens = extra.tokens; 3639 | if (typeof extra.comments !== 'undefined') { 3640 | tokens.comments = extra.comments; 3641 | } 3642 | if (typeof extra.errors !== 'undefined') { 3643 | tokens.errors = extra.errors; 3644 | } 3645 | } catch (e) { 3646 | throw e; 3647 | } finally { 3648 | extra = {}; 3649 | } 3650 | return tokens; 3651 | } 3652 | 3653 | function parse(code, options) { 3654 | var program, toString; 3655 | 3656 | toString = String; 3657 | if (typeof code !== 'string' && !(code instanceof String)) { 3658 | code = toString(code); 3659 | } 3660 | 3661 | delegate = SyntaxTreeDelegate; 3662 | source = code; 3663 | index = 0; 3664 | lineNumber = (source.length > 0) ? 1 : 0; 3665 | lineStart = 0; 3666 | length = source.length; 3667 | lookahead = null; 3668 | state = { 3669 | allowIn: true, 3670 | labelSet: {}, 3671 | inFunctionBody: false, 3672 | inIteration: false, 3673 | inSwitch: false, 3674 | lastCommentStart: -1 3675 | }; 3676 | 3677 | extra = {}; 3678 | if (typeof options !== 'undefined') { 3679 | extra.range = (typeof options.range === 'boolean') && options.range; 3680 | extra.loc = (typeof options.loc === 'boolean') && options.loc; 3681 | extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; 3682 | 3683 | if (extra.loc && options.source !== null && options.source !== undefined) { 3684 | extra.source = toString(options.source); 3685 | } 3686 | 3687 | if (typeof options.tokens === 'boolean' && options.tokens) { 3688 | extra.tokens = []; 3689 | } 3690 | if (typeof options.comment === 'boolean' && options.comment) { 3691 | extra.comments = []; 3692 | } 3693 | if (typeof options.tolerant === 'boolean' && options.tolerant) { 3694 | extra.errors = []; 3695 | } 3696 | if (extra.attachComment) { 3697 | extra.range = true; 3698 | extra.comments = []; 3699 | extra.bottomRightStack = []; 3700 | extra.trailingComments = []; 3701 | extra.leadingComments = []; 3702 | } 3703 | } 3704 | 3705 | try { 3706 | program = parseProgram(); 3707 | if (typeof extra.comments !== 'undefined') { 3708 | program.comments = extra.comments; 3709 | } 3710 | if (typeof extra.tokens !== 'undefined') { 3711 | filterTokenLocation(); 3712 | program.tokens = extra.tokens; 3713 | } 3714 | if (typeof extra.errors !== 'undefined') { 3715 | program.errors = extra.errors; 3716 | } 3717 | } catch (e) { 3718 | throw e; 3719 | } finally { 3720 | extra = {}; 3721 | } 3722 | 3723 | return program; 3724 | } 3725 | 3726 | // Sync with *.json manifests. 3727 | exports.version = '1.2.2'; 3728 | 3729 | exports.tokenize = tokenize; 3730 | 3731 | exports.parse = parse; 3732 | 3733 | // Deep copy. 3734 | /* istanbul ignore next */ 3735 | exports.Syntax = (function () { 3736 | var name, types = {}; 3737 | 3738 | if (typeof Object.create === 'function') { 3739 | types = Object.create(null); 3740 | } 3741 | 3742 | for (name in Syntax) { 3743 | if (Syntax.hasOwnProperty(name)) { 3744 | types[name] = Syntax[name]; 3745 | } 3746 | } 3747 | 3748 | if (typeof Object.freeze === 'function') { 3749 | Object.freeze(types); 3750 | } 3751 | 3752 | return types; 3753 | }()); 3754 | 3755 | })); 3756 | /* vim: set sw=4 ts=4 et tw=80 : */ 3757 | --------------------------------------------------------------------------------