├── .gitignore ├── CHANGELOG.md ├── README.md ├── SECURITY.md ├── lerna.json ├── package.json ├── packages ├── sooho-advisory-db │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── ethereum │ │ └── solidity │ │ │ └── cve │ │ │ ├── 2018-12056.yml │ │ │ ├── 2018-12511.yml │ │ │ ├── 2018-13158.yml │ │ │ ├── 2018-14001.yml │ │ │ ├── 2018-14002.yml │ │ │ ├── 2018-15552.yml │ │ │ ├── 2018-17050.yml │ │ │ ├── 2018-17071.yml │ │ │ └── 2018-17877.yml │ ├── index.js │ ├── package.json │ └── yarn.lock ├── sooho-cli │ ├── .editorconfig │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bin │ │ ├── run │ │ └── run.cmd │ ├── package.json │ ├── src │ │ ├── commands │ │ │ ├── audit.ts │ │ │ ├── encrypt.ts │ │ │ └── generate-db.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── filters.ts │ │ │ ├── flags.ts │ │ │ └── parse-files.ts │ ├── test │ │ ├── commands │ │ │ ├── Test │ │ │ │ └── Test.sol │ │ │ ├── Vulnerable.sol │ │ │ ├── audit.test.ts │ │ │ └── encrypt.test.ts │ │ ├── mocha.opts │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── tslint.json │ └── yarn.lock ├── sooho-flattener │ ├── .eslintrc.json │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ └── flattener.js │ └── yarn.lock └── sooho-parser │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintrc │ ├── .gitignore │ ├── .gitmodules │ ├── .npmignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── scripts │ └── antlr4.sh │ ├── src │ ├── ASTBuilder.js │ ├── ErrorListener.js │ ├── antlr4 │ │ ├── BufferedTokenStream.js │ │ ├── CharStreams.js │ │ ├── CommonTokenFactory.js │ │ ├── CommonTokenStream.js │ │ ├── FileStream.js │ │ ├── InputStream.js │ │ ├── IntervalSet.js │ │ ├── LL1Analyzer.js │ │ ├── Lexer.js │ │ ├── Parser.js │ │ ├── ParserRuleContext.js │ │ ├── PredictionContext.js │ │ ├── README.md │ │ ├── Recognizer.js │ │ ├── RuleContext.js │ │ ├── Token.js │ │ ├── Utils.js │ │ ├── atn │ │ │ ├── ATN.js │ │ │ ├── ATNConfig.js │ │ │ ├── ATNConfigSet.js │ │ │ ├── ATNDeserializationOptions.js │ │ │ ├── ATNDeserializer.js │ │ │ ├── ATNSimulator.js │ │ │ ├── ATNState.js │ │ │ ├── ATNType.js │ │ │ ├── LexerATNSimulator.js │ │ │ ├── LexerAction.js │ │ │ ├── LexerActionExecutor.js │ │ │ ├── ParserATNSimulator.js │ │ │ ├── PredictionMode.js │ │ │ ├── SemanticContext.js │ │ │ ├── Transition.js │ │ │ └── index.js │ │ ├── dfa │ │ │ ├── DFA.js │ │ │ ├── DFASerializer.js │ │ │ ├── DFAState.js │ │ │ └── index.js │ │ ├── error │ │ │ ├── DiagnosticErrorListener.js │ │ │ ├── ErrorListener.js │ │ │ ├── ErrorStrategy.js │ │ │ ├── Errors.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── package.json │ │ ├── polyfills │ │ │ ├── codepointat.js │ │ │ └── fromcodepoint.js │ │ └── tree │ │ │ ├── Tree.js │ │ │ ├── Trees.js │ │ │ └── index.js │ ├── index.js │ ├── lib │ │ ├── Solidity.interp │ │ ├── Solidity.tokens │ │ ├── SolidityLexer.interp │ │ ├── SolidityLexer.js │ │ ├── SolidityLexer.tokens │ │ ├── SolidityListener.js │ │ └── SolidityParser.js │ └── tokens.js │ ├── test │ ├── ast.js │ ├── index.js │ ├── test.sol │ └── utils.js │ └── yarn.lock └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node,webstorm+all 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | ### WebStorm+all ### 77 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 78 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 79 | 80 | # User-specific stuff 81 | .idea/**/workspace.xml 82 | .idea/**/tasks.xml 83 | .idea/**/usage.statistics.xml 84 | .idea/**/dictionaries 85 | .idea/**/shelf 86 | 87 | # Sensitive or high-churn files 88 | .idea/**/dataSources/ 89 | .idea/**/dataSources.ids 90 | .idea/**/dataSources.local.xml 91 | .idea/**/sqlDataSources.xml 92 | .idea/**/dynamic.xml 93 | .idea/**/uiDesigner.xml 94 | .idea/**/dbnavigator.xml 95 | 96 | # Gradle 97 | .idea/**/gradle.xml 98 | .idea/**/libraries 99 | 100 | # Gradle and Maven with auto-import 101 | # When using Gradle or Maven with auto-import, you should exclude module files, 102 | # since they will be recreated, and may cause churn. Uncomment if using 103 | # auto-import. 104 | # .idea/modules.xml 105 | # .idea/*.iml 106 | # .idea/modules 107 | 108 | # CMake 109 | cmake-build-*/ 110 | 111 | # Mongo Explorer plugin 112 | .idea/**/mongoSettings.xml 113 | 114 | # File-based project format 115 | *.iws 116 | 117 | # IntelliJ 118 | out/ 119 | 120 | # mpeltonen/sbt-idea plugin 121 | .idea_modules/ 122 | 123 | # JIRA plugin 124 | atlassian-ide-plugin.xml 125 | 126 | # Cursive Clojure plugin 127 | .idea/replstate.xml 128 | 129 | # Crashlytics plugin (for Android Studio and IntelliJ) 130 | com_crashlytics_export_strings.xml 131 | crashlytics.properties 132 | crashlytics-build.properties 133 | fabric.properties 134 | 135 | # Editor-based Rest Client 136 | .idea/httpRequests 137 | 138 | ### WebStorm+all Patch ### 139 | # Ignores the whole .idea folder and all .iml files 140 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 141 | 142 | .idea/ 143 | 144 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 145 | 146 | *.iml 147 | modules.xml 148 | .idea/misc.xml 149 | *.ipr 150 | 151 | .DS_Store 152 | .webpack 153 | 154 | 155 | 156 | # End of https://www.gitignore.io/api/node,webstorm+all 157 | .DS_Store 158 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.4](https://github.com/soohoio/sooho/compare/v0.3.3...v0.3.4) (2020-04-28) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **lerna:** track package-lock.json on git ([9efd395](https://github.com/soohoio/sooho/commit/9efd395fe8e3b5acf55f676c9a96036b1f457dca)) 12 | * **parser:** inject space between tokens @ getText ([622e9e1](https://github.com/soohoio/sooho/commit/622e9e11c6924bffc578fb73afdbe132a49b1b78)) 13 | * **parser:** relative path to token def. file ([36671a5](https://github.com/soohoio/sooho/commit/36671a52ef9c9409a410027920f45812c9660155)) 14 | * **util:** remove unused parameter in parse-files ([9591425](https://github.com/soohoio/sooho/commit/95914254effaf9e576f23d9f4a0e8c0aba7d2941)) 15 | 16 | 17 | ### Features 18 | 19 | * **cli:** add command for generating advisory db ([faaadba](https://github.com/soohoio/sooho/commit/faaadba564401bc9cbef8460a219f45feb3b181c)) 20 | * **flattener:** Add new package @sooho/sooho-flattener ([497cd7f](https://github.com/soohoio/sooho/commit/497cd7f09b7fc0776b0fd0c2df5e128e8c8e7323)) 21 | * **signature:** update signature generation logic ([5173827](https://github.com/soohoio/sooho/commit/517382715617af124864d93e460869daf5e8d36f)) 22 | 23 | 24 | 25 | 26 | 27 | ## [0.3.3](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.3) (2018-12-19) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 33 | * **config:** Change lerna config to versioning in release branch ([df6cb16](https://github.com/soohoio/sooho/commit/df6cb16)) 34 | * **package:** Add dependent libary ([abc65c3](https://github.com/soohoio/sooho/commit/abc65c3)) 35 | * **publish:** Change publish config from private to public ([e246551](https://github.com/soohoio/sooho/commit/e246551)) 36 | 37 | 38 | ### Features 39 | 40 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 41 | 42 | 43 | 44 | 45 | 46 | ## [0.3.2](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.2) (2018-12-19) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 52 | * **config:** Change lerna config to versioning in release branch ([7f54a18](https://github.com/soohoio/sooho/commit/7f54a18)) 53 | 54 | 55 | ### Features 56 | 57 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 58 | 59 | 60 | 61 | 62 | 63 | ## [0.3.1](https://github.com/soohoio/sooho/compare/v0.3.0...v0.3.1) (2018-12-16) 64 | 65 | 66 | ### Features 67 | 68 | * **lines:** Add fileinfo details ([c2194cc](https://github.com/soohoio/sooho/commit/c2194cc)) 69 | 70 | 71 | 72 | 73 | 74 | ## [0.3.1](https://github.com/soohoio/sooho/compare/v0.3.0...v0.3.1) (2018-12-16) 75 | 76 | 77 | ### Features 78 | 79 | * **lines:** Add fileinfo details ([c2194cc](https://github.com/soohoio/sooho/commit/c2194cc)) 80 | 81 | 82 | 83 | 84 | 85 | # [0.3.0](https://github.com/soohoio/sooho/compare/v0.2.0...v0.3.0) (2018-12-16) 86 | 87 | 88 | ### Features 89 | 90 | * **recursive:** Support spinner with recursive ([82dbf37](https://github.com/soohoio/sooho/commit/82dbf37)) 91 | 92 | 93 | 94 | 95 | 96 | # [0.2.0](https://github.com/soohoio/sooho/compare/v0.1.1...v0.2.0) (2018-10-16) 97 | 98 | 99 | ### Features 100 | 101 | * **macos:** Support auto-update with macos installer ([bb363f1](https://github.com/soohoio/sooho/commit/bb363f1)) 102 | 103 | 104 | 105 | 106 | 107 | ## [0.1.1](https://github.com/soohoio/sooho/compare/v0.1.0...v0.1.1) (2018-10-16) 108 | 109 | 110 | ### Features 111 | 112 | * **readme:** Support save options ([37f6ec3](https://github.com/soohoio/sooho/commit/37f6ec3)) 113 | 114 | 115 | 116 | 117 | 118 | # [0.1.0](https://github.com/soohoio/sooho/compare/v0.0.2-alpha.0...v0.1.0) (2018-10-15) 119 | 120 | 121 | ### Code Refactoring 122 | 123 | * **encrypt:** Change output format ([3ab87dc](https://github.com/soohoio/sooho/commit/3ab87dc)) 124 | 125 | 126 | ### BREAKING CHANGES 127 | 128 | * **encrypt:** JSON string format changed 129 | 130 | 131 | 132 | 133 | 134 | # Change Log 135 | 136 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 137 | 138 | 139 | # 0.1.0 (2018-10-14) 140 | 141 | 142 | ### Features 143 | 144 | * **init:** initial commit ([8b68b22](https://github.com/soohoio/sooho/commit/8b68b22)) 145 | 146 | 147 | 148 | 149 | # [0.0.2](https://github.com/soohoio/sooho/compare/v0.0.1...v0.0.2) (2018-10-14) 150 | 151 | ### Features 152 | 153 | * **init:** initial commit ([8b68b22](https://github.com/soohoio/sooho/commit/8b68b22)) 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOOHO 2 | 3 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) 4 | 5 | **Got a Question?** Send me a mail 6 | 7 | ## Table of Contents 8 | - [Introduction](#introduction) 9 | - [Development](#development) 10 | - [Install](#install) 11 | - [Test](#test) 12 | - [Run](#run) 13 | - [License](#license) 14 | - [About](#about) 15 | 16 | ## Introduction 17 | 18 | **Sooho** used to interact with Sooho services from the command line. It is built using oclif. 19 | 20 | For more about Sooho, see https://sooho.io 21 | 22 | ## Development 23 | 24 | ### Install 25 | 26 | This project is built with [lerna](http://lernajs.io/). The core plugins are located in ./packages. 27 | 28 | ```bash 29 | $ git clone https://github.com/soohoio/sooho 30 | $ lerna bootstrap 31 | $ lerna run build 32 | $ lerna run prepack 33 | ``` 34 | 35 | ### Test 36 | 37 | ```bash 38 | $ lerna run test 39 | ``` 40 | 41 | ### Run 42 | 43 | 1. Audit smart contract 44 | 45 | ``` 46 | USAGE 47 | $ lerna run audit -- INPUT_PATH 48 | 49 | ARGUMENTS 50 | INPUT_PATH entry path 51 | 52 | EXAMPLE 53 | $ lerna run audit -- ./test/commands/Vulnerable.sol 54 | ``` 55 | 56 | ## License 57 | 58 | Copyright (c) 2018 SOOHO Inc. It is free software and maybe redistributed under the terms specified in the LICENSE file. 59 | 60 | ## About 61 | 62 | Authored and maintained by **Jisu Park**. 63 | 64 | > Github [@JisuPark](https://github.com/JisuPark) 65 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | Sooho strongly believes the responsible disclosure of security vulnerabilities in open source helps user privacy and security. We appreciate your efforts to responsibly disclose your findings and will make every effort to acknowledge your contributions. 4 | 5 | To report a security issue, email [security@sooho.io](mailto:security@sooho.io) and include the word "SECURITY" in the subject line. 6 | 7 | We will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement and may ask for additional information or guidance. 8 | 9 | Report the security bugs in third-party modules to the person or team maintaining the module. You can also report a vulnerability through the [Sooho Vulnerability Disclosure](https://goo.gl/forms/BFB76kc0q0Q2a7PN2) program. 10 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.3.4", 6 | "lerna": "3.4.3", 7 | "command": { 8 | "publish": { 9 | "allowBranch": [ 10 | "master", 11 | "release/*" 12 | ], 13 | "conventionalCommits": true, 14 | "message": "chore(release): publish %s" 15 | } 16 | }, 17 | "ignoreChanges": [ 18 | "**/test/**", 19 | "**/*.md" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sooho", 3 | "description": "SOOHO CLI", 4 | "repository": "https://github.com/soohoio/sooho", 5 | "author": "Jisu Park", 6 | "private": true, 7 | "devDependencies": { 8 | "commitizen": "^3.0.2", 9 | "commitplease": "^3.2.0", 10 | "cz-conventional-changelog": "^2.1.0", 11 | "lerna": "^3.4.3", 12 | "standard-version": "^4.4.0" 13 | }, 14 | "scripts": { 15 | "commit": "git-cz", 16 | "release": "lerna publish" 17 | }, 18 | "commitplease": { 19 | "style": "angular", 20 | "nohook": true 21 | }, 22 | "config": { 23 | "commitizen": { 24 | "path": "node_modules/cz-conventional-changelog" 25 | } 26 | }, 27 | "standard-version": { 28 | "skip": { 29 | "tag": true 30 | } 31 | }, 32 | "version": "0.1.0" 33 | } 34 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.4](https://github.com/soohoio/sooho/compare/v0.3.3...v0.3.4) (2020-04-28) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **lerna:** track package-lock.json on git ([9efd395](https://github.com/soohoio/sooho/commit/9efd395fe8e3b5acf55f676c9a96036b1f457dca)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.3.3](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.3) (2018-12-19) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 23 | * **config:** Change lerna config to versioning in release branch ([df6cb16](https://github.com/soohoio/sooho/commit/df6cb16)) 24 | * **package:** Add dependent libary ([abc65c3](https://github.com/soohoio/sooho/commit/abc65c3)) 25 | * **publish:** Change publish config from private to public ([e246551](https://github.com/soohoio/sooho/commit/e246551)) 26 | 27 | 28 | ### Features 29 | 30 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 31 | 32 | 33 | 34 | 35 | 36 | ## [0.3.2](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.2) (2018-12-19) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 42 | 43 | 44 | ### Features 45 | 46 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 47 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/README.md: -------------------------------------------------------------------------------- 1 | @sooho/sooho-advisory-db 2 | == 3 | 4 | 5 | This database contains information regarding CVE(s) that affect smart contracts. We currently store version information corresponding to respective modules as understood by select sources. 6 | 7 | |Language|Environments|Metadata| 8 | |--------|-------------|--------| 9 | |Solidity|Ethereum|`name`, `address`| 10 | 11 | This project is inspired by the great work done by the people at [RubySec](http://rubysec.github.io/) and [Victims](https://blog.victi.ms/). 12 | 13 | ### Notes on CVE(s) 14 | If you already have a CVE assigned to your project and would like us to create an entry for it with the correct coordinates, you can send us a [pull request](https://help.github.com/articles/using-pull-requests) or create an issue [here](https://github.com/soohoio/sooho/issues) with CVE details and affected components. 15 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-12056.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-12056 2 | title: "Breking PRNG in All For One" 3 | description: > 4 | The maxRandom function of a smart contract implementation for All For One, an Ethereum gambling game, generates a random value with publicly readable variables because the _seed value can be retrieved with a getStorageAt call. Therefore, it allows attackers to always win and get rewards. 5 | references: 6 | - https://medium.com/coinmonks/to-be-a-winner-of-ethereum-gambling-game-all-for-one-by-breaking-prng-1ab011163d40 7 | - https://nvd.nist.gov/vuln/detail/CVE-2018-12056 8 | credits: "Jonghyuk Song" 9 | vulnerability_type: 10 | cwe: "CWE-338" 11 | swc: "SWC-120" 12 | severity: 7.5 13 | affected: 14 | contractName: "Lottery" 15 | address: "0x9c16cbb9f9bd827f6cbee556c7801073fbac2250" 16 | signature: "a9e7573645bdfd6e95d63117c6dab3ab" 17 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-12511.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-12511 2 | title: "Integer overflow" 3 | description: > 4 | In the mintToken function of a smart contract implementation for Substratum (SUB), an Ethereum ERC20 token, the administrator can control mintedAmount, leverage an integer overflow, and modify a user account's balance arbitrarily. 5 | references: 6 | - https://github.com/n0pn0pn0p/smart_contract_-vulnerability/blob/master/PolyAi.md 7 | credits: "n0pn0pn0p" 8 | vulnerability_type: 9 | cwe: "CWE-190" 10 | swc: "SWC-101" 11 | severity: 7.5 12 | affected: 13 | contractName: "MyAdvancedToken" 14 | address: "0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a" 15 | signature: "f2dea3f6ecb1397e9005efbd42dc90aa" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-13158.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-12056 2 | title: "Integer overflow" 3 | description: > 4 | The mintToken function of a smart contract implementation for AssetToken, an Ethereum token, has an integer overflow that allows the owner of the contract to set the balance of an arbitrary user to any value. 5 | references: 6 | - https://github.com/BlockChainsSecurity/EtherTokens/blob/master/GEMCHAIN/mint%20integer%20overflow.md 7 | - https://github.com/BlockChainsSecurity/EtherTokens/tree/master/AssetToken 8 | credits: "BlockChainsSecurity" 9 | vulnerability_type: 10 | cwe: "CWE-190" 11 | swc: "SWC-101" 12 | severity: 7.5 13 | affected: 14 | contractName: "AssetToken" 15 | address: "0x0bdbc0748ba09fbe9e9ed5938532e41446c2f033" 16 | signature: "8bc5f574da5fabf0b30bc2c9cb454231" 17 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-14001.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-14001 2 | title: "Integer overflow" 3 | description: > 4 | An integer overflow vulnerability exists in the function batchTransfer of SHARKTECH (SKT), an Ethereum token smart contract. An attacker could use it to set any user's balance. 5 | references: 6 | - https://github.com/VenusADLab/EtherTokens/blob/master/SHARKTECH/SHARKTECH.md 7 | credits: "VenusADLab" 8 | vulnerability_type: 9 | cwe: "CWE-190" 10 | swc: "SWC-101" 11 | severity: 7.5 12 | affected: 13 | contractName: "Shark" 14 | address: "0xf4ac238121585456DeE1096fED287F4d8906D519" 15 | signature: "6796f60784392dd620c85d43ba4f734d" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-14002.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-14002 2 | title: "Integer overflow" 3 | description: > 4 | An integer overflow vulnerability exists in the function distribute of MP3 Coin (MP3), an Ethereum token smart contract. An attacker could use it to set any user's balance. 5 | references: 6 | - https://github.com/VenusADLab/EtherTokens/blob/master/SHARKTECH/SHARKTECH.md 7 | credits: "VenusADLab" 8 | vulnerability_type: 9 | cwe: "CWE-190" 10 | swc: "SWC-101" 11 | severity: 7.5 12 | affected: 13 | contractName: "MP3Coin" 14 | address: "0x5ad6dc0a267693c8a14ac9ff2a29c7d63a3d96c2" 15 | signature: "21c275adb76b36bcb05d19966466931d" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-15552.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-15552 2 | title: "Breking PRNG in Ethereum Lottery" 3 | description: > 4 | The "PayWinner" function of a simplelottery smart contract implementation for The Ethereum Lottery, an Ethereum gambling game, generates a random value with publicly readable variable "maxTickets" (which is private, yet predictable and readable by the eth.getStorageAt function). Therefore, it allows attackers to always win and get rewards. 5 | references: 6 | - https://github.com/TEAM-C4B/CVE-LIST/tree/master/CVE-2018-15552 7 | credits: "TEAM-C4B" 8 | vulnerability_type: 9 | cwe: "CWE-338" 10 | swc: "SWC-120" 11 | severity: 7.5 12 | affected: 13 | contractName: "LottoCount" 14 | address: "0x1e217adc6a6adc16e248af109ab7efa4d1bb252d" 15 | signature: "a4aa4756cc71cf9dcf673654010fc5f2" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-17050.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-17050 2 | title: "Integer overflow" 3 | description: > 4 | The mintToken function of a smart contract implementation for PolyAi (AI), an Ethereum token, has an integer overflow that allows the owner of the contract to set the balance of an arbitrary user to any value. 5 | references: 6 | - https://github.com/n0pn0pn0p/smart_contract_-vulnerability/blob/master/PolyAi.md 7 | credits: "n0pn0pn0p" 8 | vulnerability_type: 9 | cwe: "CWE-190" 10 | swc: "SWC-101" 11 | severity: 7.5 12 | affected: 13 | contractName: "PolyAi" 14 | address: "0x5121e348e897daef1eef23959ab290e5557cf274" 15 | signature: "ba41c713dfeca7d69147f8ebcc09f699" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-17071.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-17071 2 | title: "Breking PRNG in Lucky9io" 3 | description: > 4 | The fallback function of a simple lottery smart contract implementation for Lucky9io, an Ethereum gambling game, generates a random value with the publicly readable variable entry_number. This variable is private, yet it is readable by eth.getStorageAt function. Also, attackers can purchase a ticket at a low price by directly calling the fallback function with small msg.value, because the developer set the currency unit incorrectly. Therefore, it allows attackers to always win and get rewards. 5 | references: 6 | - https://github.com/TEAM-C4B/CVE-LIST/tree/master/CVE-2018-17071 7 | credits: "TEAM-C4B" 8 | vulnerability_type: 9 | cwe: "CWE-338" 10 | swc: "SWC-120" 11 | severity: 7.5 12 | affected: 13 | contractName: "lucky9io" 14 | address: "0x94f5a9cecb397a8fb837edb809bc1cd6a66ffed2" 15 | signature: "ac00ddfd6670573c81b30089ed6e4380" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/ethereum/solidity/cve/2018-17877.yml: -------------------------------------------------------------------------------- 1 | id: CVE-2018-17877 2 | title: "Breking PRNG in Greedy 599" 3 | description: > 4 | A lottery smart contract implementation for Greedy 599, an Ethereum gambling game, generates a random value that is predictable via an external contract call. The developer used the extcodesize() function to prevent a malicious contract from being called, but the attacker can bypass it by writing the core code in the constructor of their exploit code. Therefore, it allows attackers to always win and get rewards. 5 | references: 6 | - https://github.com/TEAM-C4B/CVE-LIST/tree/master/CVE-2018-17877 7 | credits: "TEAM-C4B" 8 | vulnerability_type: 9 | cwe: "CWE-338" 10 | swc: "SWC-120" 11 | severity: 7.5 12 | affected: 13 | contractName: "Greedy" 14 | address: "0x3bb5e74f7ff56e0b64d326f8ec07236aa4a07260" 15 | signature: "79c073187783d3569f266d7dc0cdc08c" 16 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const yaml = require('js-yaml'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | const appDirectory = fs.realpathSync(process.cwd()); 7 | const cveDirectory = 'ethereum/solidity/cve' 8 | const resolveOwn = relativePath => path.resolve(__dirname, relativePath); 9 | 10 | function getAdvisoryDB() { 11 | let data = []; 12 | try { 13 | const cveDir = resolveOwn(cveDirectory) 14 | fs.readdirSync(cveDir) 15 | .map(item => resolveOwn(`${cveDirectory}/${item}`)) 16 | .forEach(filePath => data.push(yaml.safeLoad( 17 | fs.readFileSync(filePath, 'utf8') 18 | ))); 19 | return data; 20 | } catch (e) { 21 | throw new Error(e); 22 | } 23 | } 24 | 25 | module.exports = { getAdvisoryDB }; 26 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sooho/advisory-db", 3 | "version": "0.3.4", 4 | "description": "A community-driven vulnerability database of smart contract", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/soohoio/sooho" 8 | }, 9 | "license": "CC BY-NC-SA-4.0", 10 | "author": "Jisu Park ", 11 | "files": [ 12 | "/ethereum/*", 13 | "/yarn.lock" 14 | ], 15 | "main": "index.js", 16 | "bugs": "https://github.com/soohoio/sooho/issues", 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "keywords": [ 21 | "SOOHO", 22 | "security", 23 | "contract", 24 | "solidity", 25 | "ethereum", 26 | "vulnerability" 27 | ], 28 | "dependencies": { 29 | "js-yaml": "^3.12.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/sooho-advisory-db/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | argparse@^1.0.7: 6 | version "1.0.10" 7 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 8 | dependencies: 9 | sprintf-js "~1.0.2" 10 | 11 | esprima@^4.0.0: 12 | version "4.0.1" 13 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 14 | 15 | js-yaml@^3.12.0: 16 | version "3.12.0" 17 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 18 | dependencies: 19 | argparse "^1.0.7" 20 | esprima "^4.0.0" 21 | 22 | sprintf-js@~1.0.2: 23 | version "1.0.3" 24 | resolved "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 25 | -------------------------------------------------------------------------------- /packages/sooho-cli/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /packages/sooho-cli/.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | /lib 6 | /oclif.manifest.json 7 | /tmp 8 | node_modules 9 | *.aegis 10 | /*.sol 11 | -------------------------------------------------------------------------------- /packages/sooho-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.4](https://github.com/soohoio/sooho/compare/v0.3.3...v0.3.4) (2020-04-28) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **lerna:** track package-lock.json on git ([9efd395](https://github.com/soohoio/sooho/commit/9efd395fe8e3b5acf55f676c9a96036b1f457dca)) 12 | * **util:** remove unused parameter in parse-files ([9591425](https://github.com/soohoio/sooho/commit/95914254effaf9e576f23d9f4a0e8c0aba7d2941)) 13 | 14 | 15 | ### Features 16 | 17 | * **cli:** add command for generating advisory db ([faaadba](https://github.com/soohoio/sooho/commit/faaadba564401bc9cbef8460a219f45feb3b181c)) 18 | * **signature:** update signature generation logic ([5173827](https://github.com/soohoio/sooho/commit/517382715617af124864d93e460869daf5e8d36f)) 19 | 20 | 21 | 22 | 23 | 24 | ## [0.3.3](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.3) (2018-12-19) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 30 | * **config:** Change lerna config to versioning in release branch ([df6cb16](https://github.com/soohoio/sooho/commit/df6cb16)) 31 | * **package:** Add dependent libary ([abc65c3](https://github.com/soohoio/sooho/commit/abc65c3)) 32 | 33 | 34 | ### Features 35 | 36 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 37 | 38 | 39 | 40 | 41 | 42 | ## [0.3.2](https://github.com/soohoio/sooho/compare/v0.3.1...v0.3.2) (2018-12-19) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * **advisory-db:** Fix demo version of cli audit and add test files ([624ba74](https://github.com/soohoio/sooho/commit/624ba74)) 48 | 49 | 50 | ### Features 51 | 52 | * **advisory:** Add new package [@sooho](https://github.com/sooho)/sooho-advisory-db ([941d518](https://github.com/soohoio/sooho/commit/941d518)) 53 | 54 | 55 | 56 | 57 | 58 | ## [0.3.1](https://github.com/soohoio/sooho/compare/v0.3.0...v0.3.1) (2018-12-16) 59 | 60 | 61 | ### Features 62 | 63 | * **lines:** Add fileinfo details ([c2194cc](https://github.com/soohoio/sooho/commit/c2194cc)) 64 | 65 | 66 | 67 | 68 | 69 | ## [0.3.1](https://github.com/soohoio/sooho/compare/v0.3.0...v0.3.1) (2018-12-16) 70 | 71 | 72 | ### Features 73 | 74 | * **lines:** Add fileinfo details ([c2194cc](https://github.com/soohoio/sooho/commit/c2194cc)) 75 | 76 | 77 | 78 | 79 | 80 | # [0.3.0](https://github.com/soohoio/sooho/compare/v0.2.0...v0.3.0) (2018-12-16) 81 | 82 | 83 | ### Features 84 | 85 | * **recursive:** Support spinner with recursive ([82dbf37](https://github.com/soohoio/sooho/commit/82dbf37)) 86 | 87 | 88 | 89 | 90 | 91 | # [0.2.0](https://github.com/soohoio/sooho/compare/v0.1.1...v0.2.0) (2018-10-16) 92 | 93 | 94 | ### Features 95 | 96 | * **macos:** Support auto-update with macos installer ([bb363f1](https://github.com/soohoio/sooho/commit/bb363f1)) 97 | 98 | 99 | 100 | 101 | 102 | ## [0.1.1](https://github.com/soohoio/sooho/compare/v0.1.0...v0.1.1) (2018-10-16) 103 | 104 | 105 | ### Features 106 | 107 | * **readme:** Support save options ([37f6ec3](https://github.com/soohoio/sooho/commit/37f6ec3)) 108 | 109 | 110 | 111 | 112 | 113 | # [0.1.0](https://github.com/soohoio/sooho/compare/v0.0.2-alpha.0...v0.1.0) (2018-10-15) 114 | 115 | 116 | ### Code Refactoring 117 | 118 | * **encrypt:** Change output format ([3ab87dc](https://github.com/soohoio/sooho/commit/3ab87dc)) 119 | 120 | 121 | ### BREAKING CHANGES 122 | 123 | * **encrypt:** JSON string format changed 124 | -------------------------------------------------------------------------------- /packages/sooho-cli/README.md: -------------------------------------------------------------------------------- 1 | @sooho/cli 2 | ========== 3 | 4 | CLI tool to interact with Sooho 5 | 6 | [![Version](https://img.shields.io/npm/v/@sooho/cli.svg)](https://npmjs.org/package/@sooho/cli) 7 | [![Downloads/week](https://img.shields.io/npm/dw/@sooho/cli.svg)](https://npmjs.org/package/@sooho/cli) 8 | [![License](https://img.shields.io/npm/l/@sooho/cli.svg)](https://github.com/soohoio/sooho/blob/master/package.json) 9 | 10 | * [Usage](#usage) 11 | * [Commands](#commands) 12 | 13 | # Usage 14 | 15 | ```sh-session 16 | $ npm install -g @sooho/cli 17 | $ sooho COMMAND 18 | running command... 19 | $ sooho (-v|--version|version) 20 | @sooho/cli/0.0.2-alpha.0 darwin-x64 node-v9.11.1 21 | $ sooho --help [COMMAND] 22 | USAGE 23 | $ sooho COMMAND 24 | ... 25 | ``` 26 | 27 | # Commands 28 | 29 | * [`sooho encrypt INPUT_PATH`](#sooho-encrypt-input-path) 30 | * [`sooho audit INPUT_PATH`](#sooho-audit-input-path) 31 | * [`sooho help [COMMAND]`](#sooho-help-command) 32 | 33 | ## `sooho encrypt INPUT_PATH` 34 | 35 | Encrypt source code into hash file 36 | 37 | ``` 38 | USAGE 39 | $ sooho encrypt INPUT_PATH 40 | 41 | ARGUMENTS 42 | INPUT_PATH entry path 43 | 44 | OPTIONS 45 | -a, --abstract turn on abstraction mode 46 | -s, --save save encrypted file 47 | -h, --help show CLI help 48 | 49 | EXAMPLE 50 | $ sooho encrypt INPUT_PATH 51 | ``` 52 | 53 | _See code: [src/commands/encrypt.ts](https://github.com/soohoio/sooho/blob/master/packages/sooho-cli/src/commands/encrypt.ts) 54 | 55 | ## `sooho audit INPUT_PATH` 56 | 57 | Audit smart contract 58 | 59 | ``` 60 | USAGE 61 | $ sooho audit INPUT_PATH 62 | 63 | ARGUMENTS 64 | INPUT_PATH entry path 65 | 66 | EXAMPLE 67 | $ sooho audit INPUT_PATH 68 | ``` 69 | 70 | _See code: [src/commands/audit.ts](https://github.com/soohoio/sooho/blob/master/packages/sooho-cli/src/commands/audit.ts) 71 | 72 | ## `sooho help [COMMAND]` 73 | 74 | display help for sooho 75 | 76 | ``` 77 | USAGE 78 | $ sooho help [COMMAND] 79 | 80 | ARGUMENTS 81 | COMMAND command to show help for 82 | 83 | OPTIONS 84 | --all see all commands in CLI 85 | ``` 86 | 87 | _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.1.3/src/commands/help.ts) 88 | -------------------------------------------------------------------------------- /packages/sooho-cli/bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('@oclif/command').run() 4 | .then(require('@oclif/command/flush')) 5 | .catch(require('@oclif/errors/handle')) 6 | -------------------------------------------------------------------------------- /packages/sooho-cli/bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /packages/sooho-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sooho/cli", 3 | "description": "CLI tool to interact with SOOHO", 4 | "version": "0.3.4", 5 | "author": "Jisu Park ", 6 | "bin": { 7 | "sooho": "./bin/run" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/soohoio/sooho" 12 | }, 13 | "bugs": "https://github.com/soohoio/sooho/issues", 14 | "dependencies": { 15 | "@oclif/command": "^1", 16 | "@oclif/config": "^1", 17 | "@oclif/plugin-help": "^2.1.4", 18 | "@oclif/plugin-update": "^1.3.9", 19 | "@sooho/advisory-db": "^0.3.4", 20 | "@sooho/parser": "^0.3.4", 21 | "@types/diff": "^4.0.2", 22 | "cli-table": "^0.3.1", 23 | "diff": "^4.0.2", 24 | "ora": "^3.0.0", 25 | "powerwalker": "^0.1.0", 26 | "tslib": "^1", 27 | "yaml": "^1.9.2" 28 | }, 29 | "devDependencies": { 30 | "@oclif/dev-cli": "^1", 31 | "@oclif/test": "^1", 32 | "@oclif/tslint": "^3", 33 | "@types/chai": "^4", 34 | "@types/mocha": "^5", 35 | "@types/node": "^10", 36 | "@types/yaml": "^1.9.7", 37 | "aws-sdk": "^2.335.0", 38 | "chai": "^4", 39 | "globby": "^8", 40 | "mocha": "^5", 41 | "nyc": "^13", 42 | "ts-node": "^7", 43 | "tslint": "^5", 44 | "typescript": "^3.0" 45 | }, 46 | "engines": { 47 | "node": ">=8.3.0" 48 | }, 49 | "files": [ 50 | "/bin", 51 | "/lib", 52 | "/npm-shrinkwrap.json", 53 | "/oclif.manifest.json", 54 | "/yarn.lock" 55 | ], 56 | "homepage": "https://github.com/soohoio/sooho", 57 | "keywords": [ 58 | "SOOHO", 59 | "security", 60 | "assessments", 61 | "contract", 62 | "solidity", 63 | "vulnerability" 64 | ], 65 | "license": "GPL-3.0", 66 | "main": "lib/index.js", 67 | "oclif": { 68 | "commands": "./lib/commands", 69 | "bin": "sooho", 70 | "plugins": [ 71 | "@oclif/plugin-help", 72 | "@oclif/plugin-update" 73 | ], 74 | "update": { 75 | "s3": { 76 | "bucket": "sooho-cli-assets" 77 | }, 78 | "node": { 79 | "version": "10.12.0" 80 | } 81 | }, 82 | "macos": { 83 | "sign": "Developer ID Installer: Jisu Park", 84 | "identifier": "com.sooho.cli" 85 | } 86 | }, 87 | "scripts": { 88 | "audit": "./bin/run audit", 89 | "postpack": "rm -f oclif.manifest.json", 90 | "posttest": "tslint -p test -t stylish", 91 | "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme", 92 | "test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"", 93 | "version": "oclif-dev readme && git add README.md" 94 | }, 95 | "types": "lib/index.d.ts", 96 | "publishConfig": { 97 | "access": "public" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/commands/audit.ts: -------------------------------------------------------------------------------- 1 | import {Command} from '@oclif/command' 2 | import {getAdvisoryDB} from '@sooho/advisory-db' 3 | import * as fs from 'fs' 4 | import * as ora from 'ora' 5 | import * as powerwalker from 'powerwalker' 6 | import * as Table from 'cli-table' 7 | import {promisify} from 'util' 8 | import {onlySolidity} from '../utils/filters' 9 | import {parseFiles} from '../utils/parse-files' 10 | 11 | export default class Audit extends Command { 12 | static description = 'Audit smart contract' 13 | 14 | static examples = [ 15 | '$ sooho audit INPUT_PATH', 16 | ] 17 | 18 | static args = [{name: 'inputPath', required: true, description: 'entry path'}] 19 | 20 | async run() { 21 | const {args: {inputPath}} = this.parse(Audit) 22 | 23 | const spinner = ora({text: 'Parse files', spinner: 'dots'}).start() 24 | const lstat = promisify(fs.lstat) 25 | const stats = await lstat(inputPath) 26 | const routes = stats.isFile() ? [inputPath] : await powerwalker(inputPath) 27 | const filePaths = routes.filter(onlySolidity) 28 | const parsed = await parseFiles(filePaths) 29 | const {errors, success: {functions, constructors}} = parsed 30 | 31 | if (errors.length > 0) { 32 | if (functions.length > 0 || constructors.length > 0) { 33 | spinner.warn('Parse files') 34 | } else { 35 | spinner.fail('Parse files') 36 | } 37 | } else { 38 | spinner.succeed('Parse files') 39 | } 40 | 41 | let isSafe = true 42 | let vulns = [] 43 | try { 44 | const signatures = functions.map(func => func.signature) 45 | const db = getAdvisoryDB() 46 | 47 | spinner.start('Checking vulnerabilities') 48 | db.forEach(cve => { 49 | if (signatures.indexOf(cve.signature) > 0) { 50 | isSafe = false 51 | vulns.push(cve) 52 | } 53 | }) 54 | 55 | if (isSafe) { 56 | spinner.succeed('Done!') 57 | } else { 58 | const table = new Table({ 59 | head: ['CVE ID', 'Type', 'Severity', 'Desc'], 60 | colWidths: [20, 10, 10, 50] 61 | }) 62 | 63 | vulns.map(vul => table.push([ 64 | vul.id, 65 | vul.vulnerability_type.swc, 66 | vul.severity, 67 | vul.description 68 | ])) 69 | 70 | spinner.fail('Vulnerabilities have detected!') 71 | this.log(table.toString()) 72 | } 73 | } catch (e) { 74 | spinner.fail(e) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/commands/encrypt.ts: -------------------------------------------------------------------------------- 1 | import {Command} from '@oclif/command' 2 | import * as fs from 'fs' 3 | import * as ora from 'ora' 4 | import * as path from 'path' 5 | import * as powerwalker from 'powerwalker' 6 | import {promisify} from 'util' 7 | import {onlySolidity} from '../utils/filters' 8 | import {abstract, help, save} from '../utils/flags' 9 | import {parseFiles} from '../utils/parse-files' 10 | 11 | export default class Encrypt extends Command { 12 | static description = 'Encrypt source code into hash file' 13 | 14 | static examples = [ 15 | '$ sooho encrypt INPUT_PATH', 16 | ] 17 | 18 | static flags = {abstract, help, save} 19 | static args = [{name: 'inputPath', required: true, description: 'entry path'}] 20 | 21 | async run() { 22 | const {args: {inputPath}, flags: {abstract, save}} = this.parse(Encrypt) 23 | 24 | const spinner = ora({text: 'Parse files', spinner: 'dots'}).start() 25 | const lstat = promisify(fs.lstat) 26 | const stats = await lstat(inputPath) 27 | const routes = stats.isFile() ? [inputPath] : await powerwalker(inputPath) 28 | const filePaths = routes.filter(onlySolidity) 29 | const parsed = await parseFiles(filePaths) 30 | const {errors, success: {functions, constructors}} = parsed 31 | 32 | if (errors.length > 0) { 33 | if (functions.length > 0 || constructors.length > 0) { 34 | spinner.warn('Parse files') 35 | } else { 36 | spinner.fail('Parse files') 37 | } 38 | } else { 39 | spinner.succeed('Parse files') 40 | } 41 | 42 | const result = JSON.stringify(parsed, null, 4) 43 | 44 | if (save) { 45 | spinner.start('Saving results') 46 | const fileName = `${path.basename(inputPath).split('.sol')[0]}.aegis` 47 | fs.writeFile(fileName, result, err => { 48 | if (err) { 49 | this.error(err) 50 | return 51 | } 52 | spinner.succeed(`${fileName} has been created`) 53 | }) 54 | } else { 55 | this.log(result) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/commands/generate-db.ts: -------------------------------------------------------------------------------- 1 | import {Command} from '@oclif/command' 2 | import {parseFiles} from '../utils/parse-files' 3 | import {promisify} from 'util' 4 | import * as path from 'path' 5 | import * as fs from 'fs' 6 | import * as Diff from 'diff' 7 | import * as YAML from 'yaml' 8 | import * as ora from 'ora' 9 | 10 | export default class GenerateDB extends Command { 11 | static description = 'Generate advisory db' 12 | 13 | static examples = [ 14 | '$ sooho generate ORIGIN_SOL_FILE_PATH PATCHED_SOL_FILE_PATH OUTPUT_FILE_PATH', 15 | ] 16 | 17 | static args = [{ 18 | name: 'originFilePath', 19 | required: true, 20 | description: 'Original vulnerable solidity file path' 21 | }, { 22 | name: 'patchedFilePath', 23 | required: true, 24 | description: 'Patched safe solidity file path' 25 | }, { 26 | name: 'outputFilePath', 27 | required: true, 28 | description: 'File path to generate yaml file will be located at' 29 | }] 30 | 31 | async run() { 32 | const readFile = promisify(fs.readFile) 33 | const writeFile = promisify(fs.writeFile) 34 | 35 | const {args: {originFilePath, patchedFilePath, outputFilePath}} = this.parse(GenerateDB) 36 | const spinner = ora({spinner: 'dots'}) 37 | const originFileName = path.basename(originFilePath, '.sol') 38 | 39 | spinner.start('Parse files') 40 | const parseResult = await parseFiles([originFilePath, patchedFilePath]) 41 | if (parseResult.errors.length) { 42 | spinner.fail('Parse files') 43 | } else { 44 | spinner.succeed('Parse files') 45 | } 46 | 47 | const originFile = await readFile(originFilePath) 48 | const patchFile = await readFile(patchedFilePath) 49 | 50 | const functions = parseResult.success.functions 51 | const funcsInOrigin = functions.filter(f => f.filePathIdx === 0) 52 | const funcsInPatch = functions.filter(f => f.filePathIdx === 1) 53 | 54 | // filter patched function only 55 | const patchedFuncs = funcsInPatch.map(f => { 56 | const { signature, contractName } = f 57 | const matchedFunc = funcsInOrigin.find(fo => fo.contractName === contractName && fo.signature === signature) 58 | if (matchedFunc) { 59 | return null 60 | } 61 | return f.functionName 62 | }).filter(f => f) 63 | 64 | spinner.start('Generate advisory db') 65 | const generateYaml = await patchedFuncs.map(async funcName => { 66 | // get original function body 67 | const originFunc = funcsInOrigin.find(f => f.functionName === funcName) 68 | const { startLine: originStart, endLine: originEnd } = originFunc.loc 69 | const originFileBody = originFile.toString() 70 | const originFuncBody = originFileBody.split('\n').slice(originStart-1, originEnd).join('\n') 71 | 72 | // get patched function body 73 | const patchFunc = funcsInPatch.find(f => f.functionName === funcName) 74 | const { startLine: patchStart, endLine: patchEnd } = patchFunc.loc 75 | const patchFileBody = patchFile.toString() 76 | const patchFuncBody = patchFileBody.split('\n').slice(patchStart-1, patchEnd).join('\n') 77 | 78 | // get vulnerable function's signature 79 | const { signature } = originFunc 80 | 81 | // generate patch information 82 | const diff = Diff.diffLines(originFuncBody, patchFuncBody) 83 | let patch = '' 84 | diff.forEach(d => { 85 | if (d.added || d.removed) { 86 | const dSlice = d.value.split('\n') 87 | dSlice.forEach(ds => patch += d.added ? `+${ds}\n` : `-${ds}\n`) 88 | } else { 89 | patch += d.value 90 | } 91 | }) 92 | 93 | // yaml format with dummy infos, signature & patch info 94 | const yaml = { 95 | id: '', 96 | title: '', 97 | description: '', 98 | references: ['', ''], 99 | credits: '', 100 | vulnerability_type: { 101 | cwe: '', 102 | swc: '', 103 | }, 104 | severity: 0.0, 105 | affected: { 106 | contractName: '', 107 | address: '', 108 | }, 109 | signature, 110 | patch, 111 | } 112 | 113 | // write yaml file into disk 114 | await writeFile(`${outputFilePath}/${originFileName}-${funcName}.yml`, YAML.stringify(yaml)) 115 | this.log(`\nadvisory-db for function ${funcName} is generated at ${outputFilePath}/${originFileName}-${funcName}.yml`) 116 | }) 117 | 118 | await Promise.all(generateYaml) 119 | spinner.succeed('Generate advisory db') 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/index.ts: -------------------------------------------------------------------------------- 1 | export {run} from '@oclif/command' 2 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/utils/filters.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | 3 | export const onlySolidity = filePath => path.extname(filePath) === '.sol' 4 | -------------------------------------------------------------------------------- /packages/sooho-cli/src/utils/flags.ts: -------------------------------------------------------------------------------- 1 | import {flags} from '@oclif/command' 2 | 3 | export const abstract = flags.boolean({ 4 | char: 'a', 5 | description: 'turn on abstraction mode', 6 | default: false 7 | }) 8 | 9 | export const help = flags.boolean({ 10 | char: 'h', 11 | description: 'show CLI help' 12 | }) 13 | 14 | export const save = flags.boolean({ 15 | char: 's', 16 | description: 'save encrypted result', 17 | default: false 18 | }) 19 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/commands/Test/Test.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract Test { 4 | struct TodoItem { 5 | bool isActive; 6 | string text; 7 | } 8 | 9 | event LogHello(); 10 | 11 | modifier simple(uint test, string tmp) { 12 | _; 13 | } 14 | 15 | TodoItem[] public todos; 16 | 17 | function length() external view simple(5, 'sasd') returns (uint) { 18 | emit LogHello(); 19 | return todos.length; 20 | } 21 | 22 | function addTodo(bool _isActive, string _text) public { 23 | TodoItem memory todo = TodoItem(_isActive, _text); 24 | todos.push(todo); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/commands/Vulnerable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | library SafeMath { 4 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 5 | if (a == 0) { 6 | return 0; 7 | } 8 | uint256 c = a * b; 9 | assert(c / a == b); 10 | return c; 11 | } 12 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 13 | // assert(b > 0); // Solidity automatically throws when dividing by 0 14 | uint256 c = a / b; 15 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 16 | return c; 17 | } 18 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 19 | assert(b <= a); 20 | return a - b; 21 | } 22 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 23 | uint256 c = a + b; 24 | assert(c >= a); 25 | return c; 26 | } 27 | } 28 | 29 | contract Ownable { 30 | address public owner; 31 | 32 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 33 | 34 | constructor() public { 35 | owner = msg.sender; 36 | } 37 | modifier onlyOwner() { 38 | require(msg.sender == owner); 39 | _; 40 | } 41 | function transferOwnership(address newOwner) public onlyOwner { 42 | require(newOwner != address(0)); 43 | emit OwnershipTransferred(owner, newOwner); 44 | owner = newOwner; 45 | } 46 | 47 | } 48 | 49 | contract Lottery is Ownable { 50 | using SafeMath for uint256; 51 | address[] private players; 52 | address[] public winners; 53 | uint256[] public payments; 54 | uint256 private feeValue; 55 | address public lastWinner; 56 | address[] private last10Winners = [0,0,0,0,0,0,0,0,0,0]; 57 | uint256 public lastPayOut; 58 | uint256 public amountRised; 59 | address public house; 60 | uint256 public round; 61 | uint256 public playValue; 62 | uint256 public roundEnds; 63 | bool public stopped; 64 | mapping (address => uint256) public payOuts; 65 | uint256 private _seed; 66 | 67 | 68 | function bitSlice(uint256 n, uint256 bits, uint256 slot) private pure returns(uint256) { 69 | uint256 offset = slot * bits; 70 | uint256 mask = uint256((2**bits) - 1) << offset; 71 | return uint256((n & mask) >> offset); 72 | } 73 | 74 | function maxRandom() private returns (uint256 randomNumber) { 75 | _seed = uint256(keccak256(_seed, blockhash(block.number - 1), block.coinbase, block.difficulty)); 76 | return _seed; 77 | } 78 | 79 | 80 | function random(uint256 upper) private returns (uint256 randomNumber) { 81 | return maxRandom() % upper; 82 | } 83 | 84 | function setHouseAddress(address _house) onlyOwner public { 85 | house = _house; 86 | } 87 | 88 | function setFee(uint256 _fee) onlyOwner public { 89 | feeValue = _fee; 90 | } 91 | 92 | function setPlayValue(uint256 _amount) onlyOwner public { 93 | playValue = _amount; 94 | } 95 | 96 | function stopLottery(bool _stop) onlyOwner public { 97 | stopped = _stop; 98 | } 99 | 100 | function produceRandom(uint256 upper) private returns (uint256) { 101 | uint256 rand = random(upper); 102 | //output = rand; 103 | return rand; 104 | } 105 | 106 | function getPayOutAmount() private view returns (uint256) { 107 | //uint256 balance = address(this).balance; 108 | uint256 fee = amountRised.mul(feeValue).div(100); 109 | return (amountRised - fee); 110 | } 111 | 112 | function draw() public { 113 | require(now > roundEnds); 114 | uint256 howMuchBets = players.length; 115 | uint256 k; 116 | lastWinner = players[produceRandom(howMuchBets)]; 117 | lastPayOut = getPayOutAmount(); 118 | 119 | winners.push(lastWinner); 120 | if (winners.length > 9) { 121 | for (uint256 i = (winners.length - 10); i < winners.length; i++) { 122 | last10Winners[k] = winners[i]; 123 | k += 1; 124 | } 125 | } 126 | 127 | payments.push(lastPayOut); 128 | payOuts[lastWinner] += lastPayOut; 129 | lastWinner.transfer(lastPayOut); 130 | 131 | players.length = 0; 132 | round += 1; 133 | amountRised = 0; 134 | roundEnds = now + (1 * 1 days); 135 | 136 | emit NewWinner(lastWinner, lastPayOut); 137 | } 138 | 139 | function play() payable public { 140 | require (msg.value == playValue); 141 | require (!stopped); 142 | if (players.length == 0) { 143 | roundEnds = now + (1 * 1 days); 144 | } 145 | if (now > roundEnds) { 146 | draw(); 147 | } 148 | players.push(msg.sender); 149 | amountRised = amountRised.add(msg.value); 150 | } 151 | 152 | function() payable public { 153 | play(); 154 | } 155 | 156 | constructor() public { 157 | house = msg.sender; 158 | feeValue = 5; 159 | playValue = 1 finney; 160 | } 161 | 162 | function getBalance() onlyOwner public { 163 | uint256 thisBalance = address(this).balance; 164 | house.transfer(thisBalance); 165 | } 166 | 167 | function getPlayersCount() public view returns (uint256) { 168 | return players.length; 169 | } 170 | 171 | function getWinnerCount() public view returns (uint256) { 172 | return winners.length; 173 | } 174 | 175 | function getPlayers() public view returns (address[]) { 176 | return players; 177 | } 178 | 179 | function last10() public view returns (address[]) { 180 | if (winners.length < 11) { 181 | return winners; 182 | } else { 183 | return last10Winners; 184 | } 185 | } 186 | event NewWinner(address _winner, uint256 _amount); 187 | event Conso(uint a, uint b); 188 | } 189 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/commands/audit.test.ts: -------------------------------------------------------------------------------- 1 | import {expect, test} from '@oclif/test' 2 | 3 | describe('audit', () => { 4 | test 5 | .stdout() 6 | .stderr() 7 | .command(['audit', 'test/commands/Vulnerable.sol']) 8 | .it('parse file', ctx => { 9 | expect(ctx.stderr.includes('Parse files')).to.equal(true) 10 | }) 11 | 12 | test 13 | .stdout() 14 | .stderr() 15 | .command(['audit', 'test/commands/Vulnerable.sol']) 16 | .it('detect vulnerabilities', ctx => { 17 | expect(ctx.stderr.includes('Vulnerabilities have detected!')).to.equal(true) 18 | expect(ctx.stdout).to.equal([ 19 | '┌────────────────────┬──────────┬──────────┬──────────────────────────────────────────────────┐', 20 | '│ CVE ID │ Type │ Severity │ Desc │', 21 | '├────────────────────┼──────────┼──────────┼──────────────────────────────────────────────────┤', 22 | '│ CVE-2018-12056 │ SWC-120 │ 7.5 │ The maxRandom function of a smart contract impl… │', 23 | '│ │ │ │ │', 24 | '└────────────────────┴──────────┴──────────┴──────────────────────────────────────────────────┘', 25 | '' 26 | ].join('\n')) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/commands/encrypt.test.ts: -------------------------------------------------------------------------------- 1 | import {expect, test} from '@oclif/test' 2 | 3 | describe('encrypt', () => { 4 | test 5 | .stdout() 6 | .stderr() 7 | .command(['encrypt', 'test/commands/Test/Test.sol']) 8 | .it('accepts file', ctx => { 9 | const result = JSON.parse(ctx.stdout) 10 | 11 | expect(result).to.deep.equal({ 12 | version: '0.3.0', 13 | errors: [], 14 | success: { 15 | constructors: [], 16 | functions: [{ 17 | filePathIdx: 0, 18 | loc: { 19 | endLine: 20, 20 | startLine: 17 21 | }, 22 | signature: "c1804baf32d7f6c23426803ffd8d9456" 23 | }, { 24 | filePathIdx: 0, 25 | loc: { 26 | endLine: 25, 27 | startLine: 22 28 | }, 29 | signature: "5e3c146b68293fe4808f0dd8c1a88c8d" 30 | } 31 | ]}, 32 | fileInfo: { 33 | files: [{ 34 | filePath: "test/commands/Test/Test.sol", 35 | lines: 26 36 | }], 37 | totalFiles: 1, 38 | totalLines: 26, 39 | totalSigns: 2 40 | } 41 | }) 42 | }) 43 | 44 | test 45 | .stdout() 46 | .stderr() 47 | .command(['encrypt', 'test/commands/Test']) 48 | .it('accepts folder', ctx => { 49 | const result = JSON.parse(ctx.stdout) 50 | 51 | expect(result).to.deep.equal({ 52 | version: '0.3.0', 53 | errors: [], 54 | success: { 55 | constructors: [], 56 | functions: [{ 57 | filePathIdx: 0, 58 | loc: { 59 | endLine: 20, 60 | startLine: 17 61 | }, 62 | signature: "c1804baf32d7f6c23426803ffd8d9456" 63 | }, { 64 | filePathIdx: 0, 65 | loc: { 66 | endLine: 25, 67 | startLine: 22 68 | }, 69 | signature: "5e3c146b68293fe4808f0dd8c1a88c8d" 70 | } 71 | ]}, 72 | fileInfo: { 73 | files: [{ 74 | filePath: "test/commands/Test/Test.sol", 75 | lines: 26 76 | }], 77 | totalFiles: 1, 78 | totalLines: 26, 79 | totalSigns: 2 80 | } 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require ts-node/register 2 | --watch-extensions ts 3 | --recursive 4 | --reporter spec 5 | --timeout 5000 6 | -------------------------------------------------------------------------------- /packages/sooho-cli/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | "noEmit": true 5 | }, 6 | "references": [ 7 | {"path": ".."} 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sooho-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "importHelpers": true, 5 | "module": "commonjs", 6 | "outDir": "lib", 7 | "rootDir": "src", 8 | "strict": false, 9 | "target": "es2017", 10 | "composite": true 11 | }, 12 | "include": [ 13 | "src/**/*" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/sooho-cli/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@oclif/tslint", 3 | "rules": { 4 | "ordered-imports": false, 5 | "no-console": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/sooho-flattener/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "globals": { 9 | "Atomics": "readonly", 10 | "SharedArrayBuffer": "readonly" 11 | }, 12 | "parserOptions": { 13 | "ecmaVersion": 2018 14 | }, 15 | "rules": { 16 | "no-console": [ 17 | "error", 18 | { 19 | "allow": [ 20 | "warn", 21 | "error" 22 | ] 23 | } 24 | ], 25 | "indent": [ 26 | "error", 27 | 2 28 | ], 29 | "linebreak-style": [ 30 | "error", 31 | "unix" 32 | ], 33 | "quotes": [ 34 | "error", 35 | "double" 36 | ], 37 | "semi": [ 38 | "error", 39 | "always" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/sooho-flattener/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /packages/sooho-flattener/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.4](https://github.com/soohoio/sooho/compare/v0.3.3...v0.3.4) (2020-04-28) 7 | 8 | 9 | ### Features 10 | 11 | * **flattener:** Add new package @sooho/sooho-flattener ([497cd7f](https://github.com/soohoio/sooho/commit/497cd7f09b7fc0776b0fd0c2df5e128e8c8e7323)) 12 | -------------------------------------------------------------------------------- /packages/sooho-flattener/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sooho/flattener", 3 | "version": "0.3.4", 4 | "description": "Flatten solidity files", 5 | "main": "index.js", 6 | "scripts": { 7 | "eslint": "eslint src" 8 | }, 9 | "author": "Sanggu Han ", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/soohoio/sooho" 13 | }, 14 | "bugs": "https://github.com/soohoio/sooho/issues", 15 | "homepage": "https://github.com/soohoio/sooho", 16 | "license": "GPL-3.0", 17 | "dependencies": { 18 | "find-node-modules": "^2.0.0", 19 | "semver": "^6.0.0", 20 | "solidity-parser-antlr": "^0.4.2" 21 | }, 22 | "private": true, 23 | "devDependencies": { 24 | "eslint": "^5.16.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/sooho-flattener/src/flattener.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const parser = require("solidity-parser-antlr"); 6 | const process = require("process"); 7 | const semver = require("semver"); 8 | const findNodeModules = require("find-node-modules"); 9 | 10 | class Graph { 11 | constructor() { 12 | this.node = {}; 13 | } 14 | 15 | add(from, to) { 16 | if (!this.node[from]) { 17 | this.node[from] = []; 18 | } 19 | 20 | this.node[from].push(to); 21 | } 22 | } 23 | 24 | function resolvePath(baseDir, filePath) { 25 | let p = path.join(baseDir, filePath); 26 | p = path.normalize(p); 27 | if (fs.existsSync(p)) { 28 | return p; 29 | } else { 30 | const nodeModules = findNodeModules(); 31 | for (let nodeModule of nodeModules) { 32 | let m = path.join(nodeModule, filePath); 33 | if (fs.existsSync(m)) { 34 | return m; 35 | } 36 | } 37 | throw new Error(`module path, ${filePath} is not found`); 38 | } 39 | } 40 | 41 | function getInfo(filePath) { 42 | let content = fs.readFileSync(filePath, "utf8"); 43 | 44 | // get ast with solidity parser 45 | let ast = parser.parse(content); 46 | let imports = []; 47 | let version; 48 | 49 | // parse import paths in file 50 | parser.visit(ast, { 51 | ImportDirective: function(node) { 52 | /* 53 | * path - import path 54 | * unitAlias / symbolAliases - maybe i can use later 55 | */ 56 | imports.push(node.path); 57 | }, 58 | PragmaDirective: function(node) { 59 | /* 60 | * value - version sepcified in current file 61 | */ 62 | version = node.value; 63 | } 64 | }); 65 | 66 | return { 67 | imports: imports, 68 | version: version 69 | }; 70 | } 71 | 72 | async function getVersion(versions) { 73 | let caret = []; 74 | let pinned = []; 75 | // unique & group 76 | [...new Set(versions)].forEach(version => { 77 | if (version.includes("^")) { 78 | caret.push(version.substring(1)); 79 | } else { 80 | pinned.push(version); 81 | } 82 | }); 83 | 84 | let version = ""; 85 | if (caret.length) { 86 | version = "^" + caret.reduce((a, b) => semver.gt(a, b) ? a : b); 87 | } 88 | 89 | if (pinned.length) { 90 | version = pinned.reduce((a, b) => semver.gt(a, b) ? a : b); 91 | } 92 | 93 | return version; 94 | } 95 | 96 | async function flatten(filePath, baseDir, log) { 97 | let visited = []; 98 | let order = []; 99 | let versions = []; 100 | let graph = new Graph(); 101 | 102 | let target = path.resolve(filePath); 103 | baseDir = path.resolve(baseDir); 104 | 105 | // topological sorting of import graph, cutting cycles 106 | const toposort = async curr => { 107 | visited.push(curr); 108 | 109 | let info = getInfo(curr); 110 | versions.push(info.version); 111 | 112 | for (let dep of info.imports) { 113 | dep = resolvePath(baseDir, dep); 114 | graph.add(curr, dep); 115 | if (!visited.includes(dep)) { 116 | await toposort(dep); 117 | } 118 | } 119 | 120 | order.push(curr); 121 | }; 122 | 123 | await toposort(target); 124 | 125 | let version = await getVersion(versions); 126 | 127 | log("pragma solidity " + version + ";\n"); 128 | 129 | const PRAGAMA_SOLIDITY_VERSION_REGEX = /^\s*pragma\ssolidity\s+(.*?)\s*;/m; 130 | const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm; 131 | 132 | for (let p of order) { 133 | let content = fs.readFileSync(p, "utf8"); 134 | let pure = content 135 | .replace(PRAGAMA_SOLIDITY_VERSION_REGEX, "") 136 | .replace(IMPORT_SOLIDITY_REGEX, ""); 137 | log("\n\n" + pure.trim()); 138 | } 139 | } 140 | 141 | async function main(args) { 142 | try { 143 | let ret = ""; 144 | await flatten(args[0], args[1], str => (ret += str)); 145 | return ret; 146 | } catch(err) { 147 | console.error(err); 148 | } 149 | } 150 | 151 | if (require.main == module) { 152 | main(process.argv.slice(2)); 153 | } 154 | 155 | module.exports = async function(filePath, baseDir) { 156 | try { 157 | let ret = ""; 158 | await flatten(filePath, baseDir, str => (ret += str)); 159 | return ret; 160 | } catch(err) { 161 | console.error(err); 162 | } 163 | }; 164 | -------------------------------------------------------------------------------- /packages/sooho-parser/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/sooho-parser/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /packages/sooho-parser/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["standard"], 3 | "plugins": [ 4 | "standard", 5 | "promise" 6 | ], 7 | "rules": { 8 | "no-var": 2, 9 | "object-curly-spacing": [2, "always"], 10 | "object-shorthand": 2, 11 | "prefer-const": 2, 12 | "max-len": 2 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/sooho-parser/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nyc_output/ 3 | dist/ 4 | -------------------------------------------------------------------------------- /packages/sooho-parser/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "solidity-antlr4"] 2 | path = solidity-antlr4 3 | url = https://github.com/solidityj/solidity-antlr4 4 | -------------------------------------------------------------------------------- /packages/sooho-parser/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | scripts/ 3 | .nyc_output/ 4 | solidity-antlr4/ 5 | -------------------------------------------------------------------------------- /packages/sooho-parser/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | -------------------------------------------------------------------------------- /packages/sooho-parser/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.4](https://github.com/soohoio/sooho/compare/v0.3.3...v0.3.4) (2020-04-28) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **lerna:** track package-lock.json on git ([9efd395](https://github.com/soohoio/sooho/commit/9efd395fe8e3b5acf55f676c9a96036b1f457dca)) 12 | * **parser:** inject space between tokens @ getText ([622e9e1](https://github.com/soohoio/sooho/commit/622e9e11c6924bffc578fb73afdbe132a49b1b78)) 13 | * **parser:** relative path to token def. file ([36671a5](https://github.com/soohoio/sooho/commit/36671a52ef9c9409a410027920f45812c9660155)) 14 | 15 | 16 | 17 | 18 | 19 | # [0.3.0](https://github.com/soohoio/sooho/compare/v0.2.0...v0.3.0) (2018-12-16) 20 | 21 | 22 | ### Features 23 | 24 | * **recursive:** Support spinner with recursive ([82dbf37](https://github.com/soohoio/sooho/commit/82dbf37)) 25 | 26 | 27 | 28 | 29 | 30 | # [0.2.0](https://github.com/soohoio/sooho/compare/v0.1.1...v0.2.0) (2018-10-16) 31 | 32 | **Note:** Version bump only for package @sooho/parser 33 | 34 | 35 | 36 | 37 | 38 | ## [0.1.1](https://github.com/soohoio/sooho/compare/v0.1.0...v0.1.1) (2018-10-16) 39 | 40 | 41 | ### Features 42 | 43 | * **readme:** Support save options ([37f6ec3](https://github.com/soohoio/sooho/commit/37f6ec3)) 44 | 45 | 46 | 47 | 48 | 49 | # [0.1.0](https://github.com/soohoio/sooho/compare/v0.0.2-alpha.0...v0.1.0) (2018-10-15) 50 | 51 | 52 | ### Code Refactoring 53 | 54 | * **encrypt:** Change output format ([3ab87dc](https://github.com/soohoio/sooho/commit/3ab87dc)) 55 | 56 | 57 | ### BREAKING CHANGES 58 | 59 | * **encrypt:** JSON string format changed 60 | -------------------------------------------------------------------------------- /packages/sooho-parser/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sooho Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/sooho-parser/README.md: -------------------------------------------------------------------------------- 1 | @sooho/parser 2 | ===================== 3 | 4 | This project is hardly forked from the federicobond's awesome project([solidity-parser-antlr/](https://github.com/federicobond/solidity-parser-antlr/)) 5 | 6 | A Solidity parser built on top of a robust [ANTLR4 grammar](https://github.com/solidityj/solidity-antlr4). 7 | 8 | ### Usage 9 | 10 | ```javascript 11 | import parser from 'solidity-parser-antlr'; 12 | 13 | var input = ` 14 | contract test { 15 | uint256 a; 16 | function f() {} 17 | } 18 | ` 19 | try { 20 | parser.parse(input) 21 | } catch (e) { 22 | if (e instanceof parser.ParserError) { 23 | console.log(e.errors) 24 | } 25 | } 26 | ``` 27 | 28 | The `parse` method also accepts a second argument which lets you specify the 29 | following options, in a style similar to the _esprima_ API: 30 | 31 | | Key | Type | Default | Description | 32 | |----------|---------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 33 | | tolerant | Boolean | false | When set to `true` it will collect syntax errors and place them in a list under the key `errors` inside the root node of the returned AST. Otherwise, it will raise a `parser.ParserError`. | 34 | | loc | Boolean | false | When set to `true`, it will add location information to each node, with start and stop keys that contain the corresponding line and column numbers. | 35 | | range | Boolean | false | When set to `true`, it will add range information to each node, which consists of a two-element array with start and stop character indexes in the input. | 36 | 37 | 38 | #### Example with location information 39 | 40 | ```javascript 41 | parser.parse('contract test { uint a; }', { loc: true }) 42 | 43 | // { type: 'SourceUnit', 44 | // children: 45 | // [ { type: 'ContractDefinition', 46 | // name: 'test', 47 | // baseContracts: [], 48 | // subNodes: [Array], 49 | // kind: 'contract', 50 | // loc: [Object] } ], 51 | // loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 24 } } } 52 | 53 | ``` 54 | 55 | #### Example using a visitor to walk over the AST 56 | 57 | ```javascript 58 | var ast = parser.parse('contract test { uint a; }') 59 | 60 | // output the path of each import found 61 | parser.visit(ast, { 62 | ImportDirective: function(node) { 63 | console.log(node.path) 64 | } 65 | }) 66 | ``` 67 | 68 | ### Author 69 | 70 | Jisu Park ([@jisupark](https://github.com/jisupark)) 71 | 72 | And thanks to Federico Bond ([@federicobond](https://github.com/federicobond)) for the previous projects 73 | 74 | ### License 75 | 76 | MIT 77 | -------------------------------------------------------------------------------- /packages/sooho-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sooho/parser", 3 | "version": "0.3.4", 4 | "description": "A Solidity parser forked from federicobond/solidity-parser-antlr", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "antlr4": "sh scripts/antlr4.sh", 8 | "build": "rm -rf dist && babel --out-dir=dist src --copy-files", 9 | "prepublishOnly": "yarn build", 10 | "prettier": "find src -name *.js | egrep -v '^src/(lib|antlr4)/' | xargs prettier --no-semi --single-quote --write", 11 | "eslint": "eslint src", 12 | "test": "nyc mocha" 13 | }, 14 | "author": "Jisu Park ", 15 | "homepage": "https://github.com/soohoio/sooho", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/soohoio/sooho.git" 19 | }, 20 | "license": "MIT", 21 | "devDependencies": { 22 | "babel-cli": "^6.26.0", 23 | "babel-preset-env": "^1.7.0", 24 | "chai": "^4.0.2", 25 | "eslint": "^4.0.0", 26 | "eslint-config-standard": "^10.2.1", 27 | "eslint-plugin-import": "^2.3.0", 28 | "eslint-plugin-node": "^5.0.0", 29 | "eslint-plugin-promise": "^3.5.0", 30 | "eslint-plugin-standard": "^3.0.1", 31 | "mocha": "^5.2.0", 32 | "nyc": "^13.1.0", 33 | "prettier": "^1.4.4", 34 | "yarn": "^1.7.0" 35 | }, 36 | "nyc": { 37 | "include": [ 38 | "src/*.js" 39 | ] 40 | }, 41 | "publishConfig": { 42 | "access": "public" 43 | }, 44 | "dependencies": { 45 | "lodash": "^4.17.15" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/sooho-parser/scripts/antlr4.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | 5 | antlr4 -Dlanguage=JavaScript solidity-antlr4/Solidity.g4 -o lib 6 | 7 | mv lib/solidity-antlr4/* src/lib/ 8 | 9 | rmdir lib/solidity-antlr4 10 | 11 | sed -i.bak -e 's/antlr4\/index/\.\.\/antlr4\/index/g' src/lib/*.js 12 | 13 | find src/lib -name '*.js.bak' -delete 14 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/ErrorListener.js: -------------------------------------------------------------------------------- 1 | const antlr4 = require('./antlr4/index') 2 | 3 | function ErrorListener() { 4 | antlr4.error.ErrorListener.call(this) 5 | this._errors = [] 6 | } 7 | 8 | ErrorListener.prototype = Object.create(antlr4.error.ErrorListener.prototype) 9 | ErrorListener.prototype.constructor = ErrorListener 10 | 11 | ErrorListener.prototype.syntaxError = function( 12 | recognizer, 13 | offendingSymbol, 14 | line, 15 | column, 16 | message 17 | ) { 18 | this._errors.push({ message, line, column }) 19 | } 20 | 21 | ErrorListener.prototype.getErrors = function() { 22 | return this._errors 23 | } 24 | 25 | ErrorListener.prototype.hasErrors = function() { 26 | return this._errors.length > 0 27 | } 28 | 29 | module.exports = ErrorListener 30 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/CharStreams.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | // 7 | 8 | var InputStream = require('./InputStream').InputStream; 9 | 10 | var isNodeJs = typeof window === 'undefined' && typeof importScripts === 'undefined'; 11 | var fs = isNodeJs ? require("fs") : null; 12 | 13 | // Utility functions to create InputStreams from various sources. 14 | // 15 | // All returned InputStreams support the full range of Unicode 16 | // up to U+10FFFF (the default behavior of InputStream only supports 17 | // code points up to U+FFFF). 18 | var CharStreams = { 19 | // Creates an InputStream from a string. 20 | fromString: function(str) { 21 | return new InputStream(str, true); 22 | }, 23 | 24 | // Asynchronously creates an InputStream from a blob given the 25 | // encoding of the bytes in that blob (defaults to 'utf8' if 26 | // encoding is null). 27 | // 28 | // Invokes onLoad(result) on success, onError(error) on 29 | // failure. 30 | fromBlob: function(blob, encoding, onLoad, onError) { 31 | var reader = FileReader(); 32 | reader.onload = function(e) { 33 | var is = new InputStream(e.target.result, true); 34 | onLoad(is); 35 | }; 36 | reader.onerror = onError; 37 | reader.readAsText(blob, encoding); 38 | }, 39 | 40 | // Creates an InputStream from a Buffer given the 41 | // encoding of the bytes in that buffer (defaults to 'utf8' if 42 | // encoding is null). 43 | fromBuffer: function(buffer, encoding) { 44 | return new InputStream(buffer.toString(encoding), true); 45 | }, 46 | 47 | // Asynchronously creates an InputStream from a file on disk given 48 | // the encoding of the bytes in that file (defaults to 'utf8' if 49 | // encoding is null). 50 | // 51 | // Invokes callback(error, result) on completion. 52 | fromPath: function(path, encoding, callback) { 53 | fs.readFile(path, encoding, function(err, data) { 54 | var is = null; 55 | if (data !== null) { 56 | is = new InputStream(data, true); 57 | } 58 | callback(err, is); 59 | }); 60 | }, 61 | 62 | // Synchronously creates an InputStream given a path to a file 63 | // on disk and the encoding of the bytes in that file (defaults to 64 | // 'utf8' if encoding is null). 65 | fromPathSync: function(path, encoding) { 66 | var data = fs.readFileSync(path, encoding); 67 | return new InputStream(data, true); 68 | } 69 | }; 70 | 71 | exports.CharStreams = CharStreams; 72 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/CommonTokenFactory.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | // 7 | 8 | // 9 | // This default implementation of {@link TokenFactory} creates 10 | // {@link CommonToken} objects. 11 | // 12 | 13 | var CommonToken = require('./Token').CommonToken; 14 | 15 | function TokenFactory() { 16 | return this; 17 | } 18 | 19 | function CommonTokenFactory(copyText) { 20 | TokenFactory.call(this); 21 | // Indicates whether {@link CommonToken//setText} should be called after 22 | // constructing tokens to explicitly set the text. This is useful for cases 23 | // where the input stream might not be able to provide arbitrary substrings 24 | // of text from the input after the lexer creates a token (e.g. the 25 | // implementation of {@link CharStream//getText} in 26 | // {@link UnbufferedCharStream} throws an 27 | // {@link UnsupportedOperationException}). Explicitly setting the token text 28 | // allows {@link Token//getText} to be called at any time regardless of the 29 | // input stream implementation. 30 | // 31 | //

32 | // The default value is {@code false} to avoid the performance and memory 33 | // overhead of copying text for every token unless explicitly requested.

34 | // 35 | this.copyText = copyText===undefined ? false : copyText; 36 | return this; 37 | } 38 | 39 | CommonTokenFactory.prototype = Object.create(TokenFactory.prototype); 40 | CommonTokenFactory.prototype.constructor = CommonTokenFactory; 41 | 42 | // 43 | // The default {@link CommonTokenFactory} instance. 44 | // 45 | //

46 | // This token factory does not explicitly copy token text when constructing 47 | // tokens.

48 | // 49 | CommonTokenFactory.DEFAULT = new CommonTokenFactory(); 50 | 51 | CommonTokenFactory.prototype.create = function(source, type, text, channel, start, stop, line, column) { 52 | var t = new CommonToken(source, type, channel, start, stop); 53 | t.line = line; 54 | t.column = column; 55 | if (text !==null) { 56 | t.text = text; 57 | } else if (this.copyText && source[1] !==null) { 58 | t.text = source[1].getText(start,stop); 59 | } 60 | return t; 61 | }; 62 | 63 | CommonTokenFactory.prototype.createThin = function(type, text) { 64 | var t = new CommonToken(null, type); 65 | t.text = text; 66 | return t; 67 | }; 68 | 69 | exports.CommonTokenFactory = CommonTokenFactory; 70 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/CommonTokenStream.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | // 9 | // This class extends {@link BufferedTokenStream} with functionality to filter 10 | // token streams to tokens on a particular channel (tokens where 11 | // {@link Token//getChannel} returns a particular value). 12 | // 13 | //

14 | // This token stream provides access to all tokens by index or when calling 15 | // methods like {@link //getText}. The channel filtering is only used for code 16 | // accessing tokens via the lookahead methods {@link //LA}, {@link //LT}, and 17 | // {@link //LB}.

18 | // 19 | //

20 | // By default, tokens are placed on the default channel 21 | // ({@link Token//DEFAULT_CHANNEL}), but may be reassigned by using the 22 | // {@code ->channel(HIDDEN)} lexer command, or by using an embedded action to 23 | // call {@link Lexer//setChannel}. 24 | //

25 | // 26 | //

27 | // Note: lexer rules which use the {@code ->skip} lexer command or call 28 | // {@link Lexer//skip} do not produce tokens at all, so input text matched by 29 | // such a rule will not be available as part of the token stream, regardless of 30 | // channel.

31 | /// 32 | 33 | var Token = require('./Token').Token; 34 | var BufferedTokenStream = require('./BufferedTokenStream').BufferedTokenStream; 35 | 36 | function CommonTokenStream(lexer, channel) { 37 | BufferedTokenStream.call(this, lexer); 38 | this.channel = channel===undefined ? Token.DEFAULT_CHANNEL : channel; 39 | return this; 40 | } 41 | 42 | CommonTokenStream.prototype = Object.create(BufferedTokenStream.prototype); 43 | CommonTokenStream.prototype.constructor = CommonTokenStream; 44 | 45 | CommonTokenStream.prototype.adjustSeekIndex = function(i) { 46 | return this.nextTokenOnChannel(i, this.channel); 47 | }; 48 | 49 | CommonTokenStream.prototype.LB = function(k) { 50 | if (k===0 || this.index-k<0) { 51 | return null; 52 | } 53 | var i = this.index; 54 | var n = 1; 55 | // find k good tokens looking backwards 56 | while (n <= k) { 57 | // skip off-channel tokens 58 | i = this.previousTokenOnChannel(i - 1, this.channel); 59 | n += 1; 60 | } 61 | if (i < 0) { 62 | return null; 63 | } 64 | return this.tokens[i]; 65 | }; 66 | 67 | CommonTokenStream.prototype.LT = function(k) { 68 | this.lazyInit(); 69 | if (k === 0) { 70 | return null; 71 | } 72 | if (k < 0) { 73 | return this.LB(-k); 74 | } 75 | var i = this.index; 76 | var n = 1; // we know tokens[pos] is a good one 77 | // find k good tokens 78 | while (n < k) { 79 | // skip off-channel tokens, but make sure to not look past EOF 80 | if (this.sync(i + 1)) { 81 | i = this.nextTokenOnChannel(i + 1, this.channel); 82 | } 83 | n += 1; 84 | } 85 | return this.tokens[i]; 86 | }; 87 | 88 | // Count EOF just once./// 89 | CommonTokenStream.prototype.getNumberOfOnChannelTokens = function() { 90 | var n = 0; 91 | this.fill(); 92 | for (var i =0; i< this.tokens.length;i++) { 93 | var t = this.tokens[i]; 94 | if( t.channel===this.channel) { 95 | n += 1; 96 | } 97 | if( t.type===Token.EOF) { 98 | break; 99 | } 100 | } 101 | return n; 102 | }; 103 | 104 | exports.CommonTokenStream = CommonTokenStream; -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/FileStream.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | // 7 | 8 | // 9 | // This is an InputStream that is loaded from a file all at once 10 | // when you construct the object. 11 | // 12 | var InputStream = require('./InputStream').InputStream; 13 | var isNodeJs = typeof window === 'undefined' && typeof importScripts === 'undefined'; 14 | var fs = isNodeJs ? require("fs") : null; 15 | 16 | function FileStream(fileName, decodeToUnicodeCodePoints) { 17 | var data = fs.readFileSync(fileName, "utf8"); 18 | InputStream.call(this, data, decodeToUnicodeCodePoints); 19 | this.fileName = fileName; 20 | return this; 21 | } 22 | 23 | FileStream.prototype = Object.create(InputStream.prototype); 24 | FileStream.prototype.constructor = FileStream; 25 | 26 | exports.FileStream = FileStream; 27 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/InputStream.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | // 7 | 8 | var Token = require('./Token').Token; 9 | require('./polyfills/codepointat'); 10 | require('./polyfills/fromcodepoint'); 11 | 12 | // Vacuum all input from a string and then treat it like a buffer. 13 | 14 | function _loadString(stream, decodeToUnicodeCodePoints) { 15 | stream._index = 0; 16 | stream.data = []; 17 | if (stream.decodeToUnicodeCodePoints) { 18 | for (var i = 0; i < stream.strdata.length; ) { 19 | var codePoint = stream.strdata.codePointAt(i); 20 | stream.data.push(codePoint); 21 | i += codePoint <= 0xFFFF ? 1 : 2; 22 | } 23 | } else { 24 | for (var i = 0; i < stream.strdata.length; i++) { 25 | var codeUnit = stream.strdata.charCodeAt(i); 26 | stream.data.push(codeUnit); 27 | } 28 | } 29 | stream._size = stream.data.length; 30 | } 31 | 32 | // If decodeToUnicodeCodePoints is true, the input is treated 33 | // as a series of Unicode code points. 34 | // 35 | // Otherwise, the input is treated as a series of 16-bit UTF-16 code 36 | // units. 37 | function InputStream(data, decodeToUnicodeCodePoints) { 38 | this.name = ""; 39 | this.strdata = data; 40 | this.decodeToUnicodeCodePoints = decodeToUnicodeCodePoints || false; 41 | _loadString(this); 42 | return this; 43 | } 44 | 45 | Object.defineProperty(InputStream.prototype, "index", { 46 | get : function() { 47 | return this._index; 48 | } 49 | }); 50 | 51 | Object.defineProperty(InputStream.prototype, "size", { 52 | get : function() { 53 | return this._size; 54 | } 55 | }); 56 | 57 | // Reset the stream so that it's in the same state it was 58 | // when the object was created *except* the data array is not 59 | // touched. 60 | // 61 | InputStream.prototype.reset = function() { 62 | this._index = 0; 63 | }; 64 | 65 | InputStream.prototype.consume = function() { 66 | if (this._index >= this._size) { 67 | // assert this.LA(1) == Token.EOF 68 | throw ("cannot consume EOF"); 69 | } 70 | this._index += 1; 71 | }; 72 | 73 | InputStream.prototype.LA = function(offset) { 74 | if (offset === 0) { 75 | return 0; // undefined 76 | } 77 | if (offset < 0) { 78 | offset += 1; // e.g., translate LA(-1) to use offset=0 79 | } 80 | var pos = this._index + offset - 1; 81 | if (pos < 0 || pos >= this._size) { // invalid 82 | return Token.EOF; 83 | } 84 | return this.data[pos]; 85 | }; 86 | 87 | InputStream.prototype.LT = function(offset) { 88 | return this.LA(offset); 89 | }; 90 | 91 | // mark/release do nothing; we have entire buffer 92 | InputStream.prototype.mark = function() { 93 | return -1; 94 | }; 95 | 96 | InputStream.prototype.release = function(marker) { 97 | }; 98 | 99 | // consume() ahead until p==_index; can't just set p=_index as we must 100 | // update line and column. If we seek backwards, just set p 101 | // 102 | InputStream.prototype.seek = function(_index) { 103 | if (_index <= this._index) { 104 | this._index = _index; // just jump; don't update stream state (line, 105 | // ...) 106 | return; 107 | } 108 | // seek forward 109 | this._index = Math.min(_index, this._size); 110 | }; 111 | 112 | InputStream.prototype.getText = function(start, stop) { 113 | if (stop >= this._size) { 114 | stop = this._size - 1; 115 | } 116 | if (start >= this._size) { 117 | return ""; 118 | } else { 119 | if (this.decodeToUnicodeCodePoints) { 120 | var result = ""; 121 | for (var i = start; i <= stop; i++) { 122 | result += String.fromCodePoint(this.data[i]); 123 | } 124 | return result; 125 | } else { 126 | return this.strdata.slice(start, stop + 1); 127 | } 128 | } 129 | }; 130 | 131 | InputStream.prototype.toString = function() { 132 | return this.strdata; 133 | }; 134 | 135 | exports.InputStream = InputStream; 136 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/IntervalSet.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | /*jslint smarttabs:true */ 7 | 8 | var Token = require('./Token').Token; 9 | 10 | /* stop is not included! */ 11 | function Interval(start, stop) { 12 | this.start = start; 13 | this.stop = stop; 14 | return this; 15 | } 16 | 17 | Interval.prototype.contains = function(item) { 18 | return item >= this.start && item < this.stop; 19 | }; 20 | 21 | Interval.prototype.toString = function() { 22 | if(this.start===this.stop-1) { 23 | return this.start.toString(); 24 | } else { 25 | return this.start.toString() + ".." + (this.stop-1).toString(); 26 | } 27 | }; 28 | 29 | 30 | Object.defineProperty(Interval.prototype, "length", { 31 | get : function() { 32 | return this.stop - this.start; 33 | } 34 | }); 35 | 36 | function IntervalSet() { 37 | this.intervals = null; 38 | this.readOnly = false; 39 | } 40 | 41 | IntervalSet.prototype.first = function(v) { 42 | if (this.intervals === null || this.intervals.length===0) { 43 | return Token.INVALID_TYPE; 44 | } else { 45 | return this.intervals[0].start; 46 | } 47 | }; 48 | 49 | IntervalSet.prototype.addOne = function(v) { 50 | this.addInterval(new Interval(v, v + 1)); 51 | }; 52 | 53 | IntervalSet.prototype.addRange = function(l, h) { 54 | this.addInterval(new Interval(l, h + 1)); 55 | }; 56 | 57 | IntervalSet.prototype.addInterval = function(v) { 58 | if (this.intervals === null) { 59 | this.intervals = []; 60 | this.intervals.push(v); 61 | } else { 62 | // find insert pos 63 | for (var k = 0; k < this.intervals.length; k++) { 64 | var i = this.intervals[k]; 65 | // distinct range -> insert 66 | if (v.stop < i.start) { 67 | this.intervals.splice(k, 0, v); 68 | return; 69 | } 70 | // contiguous range -> adjust 71 | else if (v.stop === i.start) { 72 | this.intervals[k].start = v.start; 73 | return; 74 | } 75 | // overlapping range -> adjust and reduce 76 | else if (v.start <= i.stop) { 77 | this.intervals[k] = new Interval(Math.min(i.start, v.start), Math.max(i.stop, v.stop)); 78 | this.reduce(k); 79 | return; 80 | } 81 | } 82 | // greater than any existing 83 | this.intervals.push(v); 84 | } 85 | }; 86 | 87 | IntervalSet.prototype.addSet = function(other) { 88 | if (other.intervals !== null) { 89 | for (var k = 0; k < other.intervals.length; k++) { 90 | var i = other.intervals[k]; 91 | this.addInterval(new Interval(i.start, i.stop)); 92 | } 93 | } 94 | return this; 95 | }; 96 | 97 | IntervalSet.prototype.reduce = function(k) { 98 | // only need to reduce if k is not the last 99 | if (k < this.intervalslength - 1) { 100 | var l = this.intervals[k]; 101 | var r = this.intervals[k + 1]; 102 | // if r contained in l 103 | if (l.stop >= r.stop) { 104 | this.intervals.pop(k + 1); 105 | this.reduce(k); 106 | } else if (l.stop >= r.start) { 107 | this.intervals[k] = new Interval(l.start, r.stop); 108 | this.intervals.pop(k + 1); 109 | } 110 | } 111 | }; 112 | 113 | IntervalSet.prototype.complement = function(start, stop) { 114 | var result = new IntervalSet(); 115 | result.addInterval(new Interval(start,stop+1)); 116 | for(var i=0; ii.start && v.stop=i.stop) { 163 | this.intervals.splice(k, 1); 164 | k = k - 1; // need another pass 165 | } 166 | // check for lower boundary 167 | else if(v.start"); 235 | } else { 236 | names.push("'" + String.fromCharCode(v.start) + "'"); 237 | } 238 | } else { 239 | names.push("'" + String.fromCharCode(v.start) + "'..'" + String.fromCharCode(v.stop-1) + "'"); 240 | } 241 | } 242 | if (names.length > 1) { 243 | return "{" + names.join(", ") + "}"; 244 | } else { 245 | return names[0]; 246 | } 247 | }; 248 | 249 | 250 | IntervalSet.prototype.toIndexString = function() { 251 | var names = []; 252 | for (var i = 0; i < this.intervals.length; i++) { 253 | var v = this.intervals[i]; 254 | if(v.stop===v.start+1) { 255 | if ( v.start===Token.EOF ) { 256 | names.push(""); 257 | } else { 258 | names.push(v.start.toString()); 259 | } 260 | } else { 261 | names.push(v.start.toString() + ".." + (v.stop-1).toString()); 262 | } 263 | } 264 | if (names.length > 1) { 265 | return "{" + names.join(", ") + "}"; 266 | } else { 267 | return names[0]; 268 | } 269 | }; 270 | 271 | 272 | IntervalSet.prototype.toTokenString = function(literalNames, symbolicNames) { 273 | var names = []; 274 | for (var i = 0; i < this.intervals.length; i++) { 275 | var v = this.intervals[i]; 276 | for (var j = v.start; j < v.stop; j++) { 277 | names.push(this.elementName(literalNames, symbolicNames, j)); 278 | } 279 | } 280 | if (names.length > 1) { 281 | return "{" + names.join(", ") + "}"; 282 | } else { 283 | return names[0]; 284 | } 285 | }; 286 | 287 | IntervalSet.prototype.elementName = function(literalNames, symbolicNames, a) { 288 | if (a === Token.EOF) { 289 | return ""; 290 | } else if (a === Token.EPSILON) { 291 | return ""; 292 | } else { 293 | return literalNames[a] || symbolicNames[a]; 294 | } 295 | }; 296 | 297 | exports.Interval = Interval; 298 | exports.IntervalSet = IntervalSet; 299 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/LL1Analyzer.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | var Set = require('./Utils').Set; 9 | var BitSet = require('./Utils').BitSet; 10 | var Token = require('./Token').Token; 11 | var ATNConfig = require('./atn/ATNConfig').ATNConfig; 12 | var Interval = require('./IntervalSet').Interval; 13 | var IntervalSet = require('./IntervalSet').IntervalSet; 14 | var RuleStopState = require('./atn/ATNState').RuleStopState; 15 | var RuleTransition = require('./atn/Transition').RuleTransition; 16 | var NotSetTransition = require('./atn/Transition').NotSetTransition; 17 | var WildcardTransition = require('./atn/Transition').WildcardTransition; 18 | var AbstractPredicateTransition = require('./atn/Transition').AbstractPredicateTransition; 19 | 20 | var pc = require('./PredictionContext'); 21 | var predictionContextFromRuleContext = pc.predictionContextFromRuleContext; 22 | var PredictionContext = pc.PredictionContext; 23 | var SingletonPredictionContext = pc.SingletonPredictionContext; 24 | 25 | function LL1Analyzer (atn) { 26 | this.atn = atn; 27 | } 28 | 29 | //* Special value added to the lookahead sets to indicate that we hit 30 | // a predicate during analysis if {@code seeThruPreds==false}. 31 | /// 32 | LL1Analyzer.HIT_PRED = Token.INVALID_TYPE; 33 | 34 | 35 | //* 36 | // Calculates the SLL(1) expected lookahead set for each outgoing transition 37 | // of an {@link ATNState}. The returned array has one element for each 38 | // outgoing transition in {@code s}. If the closure from transition 39 | // i leads to a semantic predicate before matching a symbol, the 40 | // element at index i of the result will be {@code null}. 41 | // 42 | // @param s the ATN state 43 | // @return the expected symbols for each outgoing transition of {@code s}. 44 | /// 45 | LL1Analyzer.prototype.getDecisionLookahead = function(s) { 46 | if (s === null) { 47 | return null; 48 | } 49 | var count = s.transitions.length; 50 | var look = []; 51 | for(var alt=0; alt< count; alt++) { 52 | look[alt] = new IntervalSet(); 53 | var lookBusy = new Set(); 54 | var seeThruPreds = false; // fail to get lookahead upon pred 55 | this._LOOK(s.transition(alt).target, null, PredictionContext.EMPTY, 56 | look[alt], lookBusy, new BitSet(), seeThruPreds, false); 57 | // Wipe out lookahead for this alternative if we found nothing 58 | // or we had a predicate when we !seeThruPreds 59 | if (look[alt].length===0 || look[alt].contains(LL1Analyzer.HIT_PRED)) { 60 | look[alt] = null; 61 | } 62 | } 63 | return look; 64 | }; 65 | 66 | //* 67 | // Compute set of tokens that can follow {@code s} in the ATN in the 68 | // specified {@code ctx}. 69 | // 70 | //

If {@code ctx} is {@code null} and the end of the rule containing 71 | // {@code s} is reached, {@link Token//EPSILON} is added to the result set. 72 | // If {@code ctx} is not {@code null} and the end of the outermost rule is 73 | // reached, {@link Token//EOF} is added to the result set.

74 | // 75 | // @param s the ATN state 76 | // @param stopState the ATN state to stop at. This can be a 77 | // {@link BlockEndState} to detect epsilon paths through a closure. 78 | // @param ctx the complete parser context, or {@code null} if the context 79 | // should be ignored 80 | // 81 | // @return The set of tokens that can follow {@code s} in the ATN in the 82 | // specified {@code ctx}. 83 | /// 84 | LL1Analyzer.prototype.LOOK = function(s, stopState, ctx) { 85 | var r = new IntervalSet(); 86 | var seeThruPreds = true; // ignore preds; get all lookahead 87 | ctx = ctx || null; 88 | var lookContext = ctx!==null ? predictionContextFromRuleContext(s.atn, ctx) : null; 89 | this._LOOK(s, stopState, lookContext, r, new Set(), new BitSet(), seeThruPreds, true); 90 | return r; 91 | }; 92 | 93 | //* 94 | // Compute set of tokens that can follow {@code s} in the ATN in the 95 | // specified {@code ctx}. 96 | // 97 | //

If {@code ctx} is {@code null} and {@code stopState} or the end of the 98 | // rule containing {@code s} is reached, {@link Token//EPSILON} is added to 99 | // the result set. If {@code ctx} is not {@code null} and {@code addEOF} is 100 | // {@code true} and {@code stopState} or the end of the outermost rule is 101 | // reached, {@link Token//EOF} is added to the result set.

102 | // 103 | // @param s the ATN state. 104 | // @param stopState the ATN state to stop at. This can be a 105 | // {@link BlockEndState} to detect epsilon paths through a closure. 106 | // @param ctx The outer context, or {@code null} if the outer context should 107 | // not be used. 108 | // @param look The result lookahead set. 109 | // @param lookBusy A set used for preventing epsilon closures in the ATN 110 | // from causing a stack overflow. Outside code should pass 111 | // {@code new Set} for this argument. 112 | // @param calledRuleStack A set used for preventing left recursion in the 113 | // ATN from causing a stack overflow. Outside code should pass 114 | // {@code new BitSet()} for this argument. 115 | // @param seeThruPreds {@code true} to true semantic predicates as 116 | // implicitly {@code true} and "see through them", otherwise {@code false} 117 | // to treat semantic predicates as opaque and add {@link //HIT_PRED} to the 118 | // result if one is encountered. 119 | // @param addEOF Add {@link Token//EOF} to the result if the end of the 120 | // outermost context is reached. This parameter has no effect if {@code ctx} 121 | // is {@code null}. 122 | /// 123 | LL1Analyzer.prototype._LOOK = function(s, stopState , ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF) { 124 | var c = new ATNConfig({state:s, alt:0, context: ctx}, null); 125 | if (lookBusy.contains(c)) { 126 | return; 127 | } 128 | lookBusy.add(c); 129 | if (s === stopState) { 130 | if (ctx ===null) { 131 | look.addOne(Token.EPSILON); 132 | return; 133 | } else if (ctx.isEmpty() && addEOF) { 134 | look.addOne(Token.EOF); 135 | return; 136 | } 137 | } 138 | if (s instanceof RuleStopState ) { 139 | if (ctx ===null) { 140 | look.addOne(Token.EPSILON); 141 | return; 142 | } else if (ctx.isEmpty() && addEOF) { 143 | look.addOne(Token.EOF); 144 | return; 145 | } 146 | if (ctx !== PredictionContext.EMPTY) { 147 | // run thru all possible stack tops in ctx 148 | for(var i=0; i= this.children.length) { 123 | return null; 124 | } 125 | if (type === null) { 126 | return this.children[i]; 127 | } else { 128 | for(var j=0; j= this.children.length) { 145 | return null; 146 | } 147 | for(var j=0; jUsed for XPath and tree pattern compilation.

55 | // 56 | Recognizer.prototype.getRuleIndexMap = function() { 57 | var ruleNames = this.ruleNames; 58 | if (ruleNames===null) { 59 | throw("The current recognizer does not provide a list of rule names."); 60 | } 61 | var result = this.ruleIndexMapCache[ruleNames]; 62 | if(result===undefined) { 63 | result = ruleNames.reduce(function(o, k, i) { o[k] = i; }); 64 | this.ruleIndexMapCache[ruleNames] = result; 65 | } 66 | return result; 67 | }; 68 | 69 | Recognizer.prototype.getTokenType = function(tokenName) { 70 | var ttype = this.getTokenTypeMap()[tokenName]; 71 | if (ttype !==undefined) { 72 | return ttype; 73 | } else { 74 | return Token.INVALID_TYPE; 75 | } 76 | }; 77 | 78 | 79 | // What is the error header, normally line/character position information?// 80 | Recognizer.prototype.getErrorHeader = function(e) { 81 | var line = e.getOffendingToken().line; 82 | var column = e.getOffendingToken().column; 83 | return "line " + line + ":" + column; 84 | }; 85 | 86 | 87 | // How should a token be displayed in an error message? The default 88 | // is to display just the text, but during development you might 89 | // want to have a lot of information spit out. Override in that case 90 | // to use t.toString() (which, for CommonToken, dumps everything about 91 | // the token). This is better than forcing you to override a method in 92 | // your token objects because you don't have to go modify your lexer 93 | // so that it creates a new Java type. 94 | // 95 | // @deprecated This method is not called by the ANTLR 4 Runtime. Specific 96 | // implementations of {@link ANTLRErrorStrategy} may provide a similar 97 | // feature when necessary. For example, see 98 | // {@link DefaultErrorStrategy//getTokenErrorDisplay}. 99 | // 100 | Recognizer.prototype.getTokenErrorDisplay = function(t) { 101 | if (t===null) { 102 | return ""; 103 | } 104 | var s = t.text; 105 | if (s===null) { 106 | if (t.type===Token.EOF) { 107 | s = ""; 108 | } else { 109 | s = "<" + t.type + ">"; 110 | } 111 | } 112 | s = s.replace("\n","\\n").replace("\r","\\r").replace("\t","\\t"); 113 | return "'" + s + "'"; 114 | }; 115 | 116 | Recognizer.prototype.getErrorListenerDispatch = function() { 117 | return new ProxyErrorListener(this._listeners); 118 | }; 119 | 120 | // subclass needs to override these if there are sempreds or actions 121 | // that the ATN interp needs to execute 122 | Recognizer.prototype.sempred = function(localctx, ruleIndex, actionIndex) { 123 | return true; 124 | }; 125 | 126 | Recognizer.prototype.precpred = function(localctx , precedence) { 127 | return true; 128 | }; 129 | 130 | //Indicate that the recognizer has changed internal state that is 131 | //consistent with the ATN state passed in. This way we always know 132 | //where we are in the ATN as the parser goes along. The rule 133 | //context objects form a stack that lets us see the stack of 134 | //invoking rules. Combine this and we have complete ATN 135 | //configuration information. 136 | 137 | Object.defineProperty(Recognizer.prototype, "state", { 138 | get : function() { 139 | return this._stateNumber; 140 | }, 141 | set : function(state) { 142 | this._stateNumber = state; 143 | } 144 | }); 145 | 146 | 147 | exports.Recognizer = Recognizer; 148 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/RuleContext.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | /// 6 | 7 | // A rule context is a record of a single rule invocation. It knows 8 | // which context invoked it, if any. If there is no parent context, then 9 | // naturally the invoking state is not valid. The parent link 10 | // provides a chain upwards from the current rule invocation to the root 11 | // of the invocation tree, forming a stack. We actually carry no 12 | // information about the rule associated with this context (except 13 | // when parsing). We keep only the state number of the invoking state from 14 | // the ATN submachine that invoked this. Contrast this with the s 15 | // pointer inside ParserRuleContext that tracks the current state 16 | // being "executed" for the current rule. 17 | // 18 | // The parent contexts are useful for computing lookahead sets and 19 | // getting error information. 20 | // 21 | // These objects are used during parsing and prediction. 22 | // For the special case of parsers, we use the subclass 23 | // ParserRuleContext. 24 | // 25 | // @see ParserRuleContext 26 | /// 27 | 28 | var RuleNode = require('./tree/Tree').RuleNode; 29 | var INVALID_INTERVAL = require('./tree/Tree').INVALID_INTERVAL; 30 | var INVALID_ALT_NUMBER = require('./atn/ATN').INVALID_ALT_NUMBER; 31 | 32 | function RuleContext(parent, invokingState) { 33 | RuleNode.call(this); 34 | // What context invoked this rule? 35 | this.parentCtx = parent || null; 36 | // What state invoked the rule associated with this context? 37 | // The "return address" is the followState of invokingState 38 | // If parent is null, this should be -1. 39 | this.invokingState = invokingState || -1; 40 | return this; 41 | } 42 | 43 | RuleContext.prototype = Object.create(RuleNode.prototype); 44 | RuleContext.prototype.constructor = RuleContext; 45 | 46 | RuleContext.prototype.depth = function() { 47 | var n = 0; 48 | var p = this; 49 | while (p !== null) { 50 | p = p.parentCtx; 51 | n += 1; 52 | } 53 | return n; 54 | }; 55 | 56 | // A context is empty if there is no invoking state; meaning nobody call 57 | // current context. 58 | RuleContext.prototype.isEmpty = function() { 59 | return this.invokingState === -1; 60 | }; 61 | 62 | // satisfy the ParseTree / SyntaxTree interface 63 | 64 | RuleContext.prototype.getSourceInterval = function() { 65 | return INVALID_INTERVAL; 66 | }; 67 | 68 | RuleContext.prototype.getRuleContext = function() { 69 | return this; 70 | }; 71 | 72 | RuleContext.prototype.getPayload = function() { 73 | return this; 74 | }; 75 | 76 | // Return the combined text of all child nodes. This method only considers 77 | // tokens which have been added to the parse tree. 78 | //

79 | // Since tokens on hidden channels (e.g. whitespace or comments) are not 80 | // added to the parse trees, they will not appear in the output of this 81 | // method. 82 | // / 83 | RuleContext.prototype.getText = function() { 84 | if (this.getChildCount() === 0) { 85 | return ""; 86 | } else { 87 | return this.children.map(function(child) { 88 | return child.getText(); 89 | }).join(" "); 90 | } 91 | }; 92 | 93 | // For rule associated with this parse tree internal node, return 94 | // the outer alternative number used to match the input. Default 95 | // implementation does not compute nor store this alt num. Create 96 | // a subclass of ParserRuleContext with backing field and set 97 | // option contextSuperClass. 98 | // to set it. 99 | RuleContext.prototype.getAltNumber = function() { return INVALID_ALT_NUMBER; } 100 | 101 | // Set the outer alternative number for this context node. Default 102 | // implementation does nothing to avoid backing field overhead for 103 | // trees that don't need it. Create 104 | // a subclass of ParserRuleContext with backing field and set 105 | // option contextSuperClass. 106 | RuleContext.prototype.setAltNumber = function(altNumber) { } 107 | 108 | RuleContext.prototype.getChild = function(i) { 109 | return null; 110 | }; 111 | 112 | RuleContext.prototype.getChildCount = function() { 113 | return 0; 114 | }; 115 | 116 | RuleContext.prototype.accept = function(visitor) { 117 | return visitor.visitChildren(this); 118 | }; 119 | 120 | //need to manage circular dependencies, so export now 121 | exports.RuleContext = RuleContext; 122 | var Trees = require('./tree/Trees').Trees; 123 | 124 | 125 | // Print out a whole tree, not just a node, in LISP format 126 | // (root child1 .. childN). Print just a node if this is a leaf. 127 | // 128 | 129 | RuleContext.prototype.toStringTree = function(ruleNames, recog) { 130 | return Trees.toStringTree(this, ruleNames, recog); 131 | }; 132 | 133 | RuleContext.prototype.toString = function(ruleNames, stop) { 134 | ruleNames = ruleNames || null; 135 | stop = stop || null; 136 | var p = this; 137 | var s = "["; 138 | while (p !== null && p !== stop) { 139 | if (ruleNames === null) { 140 | if (!p.isEmpty()) { 141 | s += p.invokingState; 142 | } 143 | } else { 144 | var ri = p.ruleIndex; 145 | var ruleName = (ri >= 0 && ri < ruleNames.length) ? ruleNames[ri] 146 | : "" + ri; 147 | s += ruleName; 148 | } 149 | if (p.parentCtx !== null && (ruleNames !== null || !p.parentCtx.isEmpty())) { 150 | s += " "; 151 | } 152 | p = p.parentCtx; 153 | } 154 | s += "]"; 155 | return s; 156 | }; 157 | 158 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/Token.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | // 6 | 7 | // A token has properties: text, type, line, character position in the line 8 | // (so we can ignore tabs), token channel, index, and source from which 9 | // we obtained this token. 10 | 11 | function Token() { 12 | this.source = null; 13 | this.type = null; // token type of the token 14 | this.channel = null; // The parser ignores everything not on DEFAULT_CHANNEL 15 | this.start = null; // optional; return -1 if not implemented. 16 | this.stop = null; // optional; return -1 if not implemented. 17 | this.tokenIndex = null; // from 0..n-1 of the token object in the input stream 18 | this.line = null; // line=1..n of the 1st character 19 | this.column = null; // beginning of the line at which it occurs, 0..n-1 20 | this._text = null; // text of the token. 21 | return this; 22 | } 23 | 24 | Token.INVALID_TYPE = 0; 25 | 26 | // During lookahead operations, this "token" signifies we hit rule end ATN state 27 | // and did not follow it despite needing to. 28 | Token.EPSILON = -2; 29 | 30 | Token.MIN_USER_TOKEN_TYPE = 1; 31 | 32 | Token.EOF = -1; 33 | 34 | // All tokens go to the parser (unless skip() is called in that rule) 35 | // on a particular "channel". The parser tunes to a particular channel 36 | // so that whitespace etc... can go to the parser on a "hidden" channel. 37 | 38 | Token.DEFAULT_CHANNEL = 0; 39 | 40 | // Anything on different channel than DEFAULT_CHANNEL is not parsed 41 | // by parser. 42 | 43 | Token.HIDDEN_CHANNEL = 1; 44 | 45 | // Explicitly set the text for this token. If {code text} is not 46 | // {@code null}, then {@link //getText} will return this value rather than 47 | // extracting the text from the input. 48 | // 49 | // @param text The explicit text of the token, or {@code null} if the text 50 | // should be obtained from the input along with the start and stop indexes 51 | // of the token. 52 | 53 | Object.defineProperty(Token.prototype, "text", { 54 | get : function() { 55 | return this._text; 56 | }, 57 | set : function(text) { 58 | this._text = text; 59 | } 60 | }); 61 | 62 | Token.prototype.getTokenSource = function() { 63 | return this.source[0]; 64 | }; 65 | 66 | Token.prototype.getInputStream = function() { 67 | return this.source[1]; 68 | }; 69 | 70 | function CommonToken(source, type, channel, start, stop) { 71 | Token.call(this); 72 | this.source = source !== undefined ? source : CommonToken.EMPTY_SOURCE; 73 | this.type = type !== undefined ? type : null; 74 | this.channel = channel !== undefined ? channel : Token.DEFAULT_CHANNEL; 75 | this.start = start !== undefined ? start : -1; 76 | this.stop = stop !== undefined ? stop : -1; 77 | this.tokenIndex = -1; 78 | if (this.source[0] !== null) { 79 | this.line = source[0].line; 80 | this.column = source[0].column; 81 | } else { 82 | this.column = -1; 83 | } 84 | return this; 85 | } 86 | 87 | CommonToken.prototype = Object.create(Token.prototype); 88 | CommonToken.prototype.constructor = CommonToken; 89 | 90 | // An empty {@link Pair} which is used as the default value of 91 | // {@link //source} for tokens that do not have a source. 92 | CommonToken.EMPTY_SOURCE = [ null, null ]; 93 | 94 | // Constructs a new {@link CommonToken} as a copy of another {@link Token}. 95 | // 96 | //

97 | // If {@code oldToken} is also a {@link CommonToken} instance, the newly 98 | // constructed token will share a reference to the {@link //text} field and 99 | // the {@link Pair} stored in {@link //source}. Otherwise, {@link //text} will 100 | // be assigned the result of calling {@link //getText}, and {@link //source} 101 | // will be constructed from the result of {@link Token//getTokenSource} and 102 | // {@link Token//getInputStream}.

103 | // 104 | // @param oldToken The token to copy. 105 | // 106 | CommonToken.prototype.clone = function() { 107 | var t = new CommonToken(this.source, this.type, this.channel, this.start, 108 | this.stop); 109 | t.tokenIndex = this.tokenIndex; 110 | t.line = this.line; 111 | t.column = this.column; 112 | t.text = this.text; 113 | return t; 114 | }; 115 | 116 | Object.defineProperty(CommonToken.prototype, "text", { 117 | get : function() { 118 | if (this._text !== null) { 119 | return this._text; 120 | } 121 | var input = this.getInputStream(); 122 | if (input === null) { 123 | return null; 124 | } 125 | var n = input.size; 126 | if (this.start < n && this.stop < n) { 127 | return input.getText(this.start, this.stop); 128 | } else { 129 | return ""; 130 | } 131 | }, 132 | set : function(text) { 133 | this._text = text; 134 | } 135 | }); 136 | 137 | CommonToken.prototype.toString = function() { 138 | var txt = this.text; 139 | if (txt !== null) { 140 | txt = txt.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t"); 141 | } else { 142 | txt = ""; 143 | } 144 | return "[@" + this.tokenIndex + "," + this.start + ":" + this.stop + "='" + 145 | txt + "',<" + this.type + ">" + 146 | (this.channel > 0 ? ",channel=" + this.channel : "") + "," + 147 | this.line + ":" + this.column + "]"; 148 | }; 149 | 150 | exports.Token = Token; 151 | exports.CommonToken = CommonToken; 152 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/ATN.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | var LL1Analyzer = require('./../LL1Analyzer').LL1Analyzer; 7 | var IntervalSet = require('./../IntervalSet').IntervalSet; 8 | 9 | function ATN(grammarType , maxTokenType) { 10 | 11 | // Used for runtime deserialization of ATNs from strings/// 12 | // The type of the ATN. 13 | this.grammarType = grammarType; 14 | // The maximum value for any symbol recognized by a transition in the ATN. 15 | this.maxTokenType = maxTokenType; 16 | this.states = []; 17 | // Each subrule/rule is a decision point and we must track them so we 18 | // can go back later and build DFA predictors for them. This includes 19 | // all the rules, subrules, optional blocks, ()+, ()* etc... 20 | this.decisionToState = []; 21 | // Maps from rule index to starting state number. 22 | this.ruleToStartState = []; 23 | // Maps from rule index to stop state number. 24 | this.ruleToStopState = null; 25 | this.modeNameToStartState = {}; 26 | // For lexer ATNs, this maps the rule index to the resulting token type. 27 | // For parser ATNs, this maps the rule index to the generated bypass token 28 | // type if the 29 | // {@link ATNDeserializationOptions//isGenerateRuleBypassTransitions} 30 | // deserialization option was specified; otherwise, this is {@code null}. 31 | this.ruleToTokenType = null; 32 | // For lexer ATNs, this is an array of {@link LexerAction} objects which may 33 | // be referenced by action transitions in the ATN. 34 | this.lexerActions = null; 35 | this.modeToStartState = []; 36 | 37 | return this; 38 | } 39 | 40 | // Compute the set of valid tokens that can occur starting in state {@code s}. 41 | // If {@code ctx} is null, the set of tokens will not include what can follow 42 | // the rule surrounding {@code s}. In other words, the set will be 43 | // restricted to tokens reachable staying within {@code s}'s rule. 44 | ATN.prototype.nextTokensInContext = function(s, ctx) { 45 | var anal = new LL1Analyzer(this); 46 | return anal.LOOK(s, null, ctx); 47 | }; 48 | 49 | // Compute the set of valid tokens that can occur starting in {@code s} and 50 | // staying in same rule. {@link Token//EPSILON} is in set if we reach end of 51 | // rule. 52 | ATN.prototype.nextTokensNoContext = function(s) { 53 | if (s.nextTokenWithinRule !== null ) { 54 | return s.nextTokenWithinRule; 55 | } 56 | s.nextTokenWithinRule = this.nextTokensInContext(s, null); 57 | s.nextTokenWithinRule.readOnly = true; 58 | return s.nextTokenWithinRule; 59 | }; 60 | 61 | ATN.prototype.nextTokens = function(s, ctx) { 62 | if ( ctx===undefined ) { 63 | return this.nextTokensNoContext(s); 64 | } else { 65 | return this.nextTokensInContext(s, ctx); 66 | } 67 | }; 68 | 69 | ATN.prototype.addState = function( state) { 70 | if ( state !== null ) { 71 | state.atn = this; 72 | state.stateNumber = this.states.length; 73 | } 74 | this.states.push(state); 75 | }; 76 | 77 | ATN.prototype.removeState = function( state) { 78 | this.states[state.stateNumber] = null; // just free mem, don't shift states in list 79 | }; 80 | 81 | ATN.prototype.defineDecisionState = function( s) { 82 | this.decisionToState.push(s); 83 | s.decision = this.decisionToState.length-1; 84 | return s.decision; 85 | }; 86 | 87 | ATN.prototype.getDecisionState = function( decision) { 88 | if (this.decisionToState.length===0) { 89 | return null; 90 | } else { 91 | return this.decisionToState[decision]; 92 | } 93 | }; 94 | 95 | // Computes the set of input symbols which could follow ATN state number 96 | // {@code stateNumber} in the specified full {@code context}. This method 97 | // considers the complete parser context, but does not evaluate semantic 98 | // predicates (i.e. all predicates encountered during the calculation are 99 | // assumed true). If a path in the ATN exists from the starting state to the 100 | // {@link RuleStopState} of the outermost context without matching any 101 | // symbols, {@link Token//EOF} is added to the returned set. 102 | // 103 | //

If {@code context} is {@code null}, it is treated as 104 | // {@link ParserRuleContext//EMPTY}.

105 | // 106 | // @param stateNumber the ATN state number 107 | // @param context the full parse context 108 | // @return The set of potentially valid input symbols which could follow the 109 | // specified state in the specified context. 110 | // @throws IllegalArgumentException if the ATN does not contain a state with 111 | // number {@code stateNumber} 112 | var Token = require('./../Token').Token; 113 | 114 | ATN.prototype.getExpectedTokens = function( stateNumber, ctx ) { 115 | if ( stateNumber < 0 || stateNumber >= this.states.length ) { 116 | throw("Invalid state number."); 117 | } 118 | var s = this.states[stateNumber]; 119 | var following = this.nextTokens(s); 120 | if (!following.contains(Token.EPSILON)) { 121 | return following; 122 | } 123 | var expected = new IntervalSet(); 124 | expected.addSet(following); 125 | expected.removeOne(Token.EPSILON); 126 | while (ctx !== null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) { 127 | var invokingState = this.states[ctx.invokingState]; 128 | var rt = invokingState.transitions[0]; 129 | following = this.nextTokens(rt.followState); 130 | expected.addSet(following); 131 | expected.removeOne(Token.EPSILON); 132 | ctx = ctx.parentCtx; 133 | } 134 | if (following.contains(Token.EPSILON)) { 135 | expected.addOne(Token.EOF); 136 | } 137 | return expected; 138 | }; 139 | 140 | ATN.INVALID_ALT_NUMBER = 0; 141 | 142 | exports.ATN = ATN; -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/ATNConfig.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | // A tuple: (ATN state, predicted alt, syntactic, semantic context). 9 | // The syntactic context is a graph-structured stack node whose 10 | // path(s) to the root is the rule invocation(s) 11 | // chain used to arrive at the state. The semantic context is 12 | // the tree of semantic predicates encountered before reaching 13 | // an ATN state. 14 | /// 15 | 16 | var DecisionState = require('./ATNState').DecisionState; 17 | var SemanticContext = require('./SemanticContext').SemanticContext; 18 | var Hash = require("../Utils").Hash; 19 | 20 | 21 | function checkParams(params, isCfg) { 22 | if(params===null) { 23 | var result = { state:null, alt:null, context:null, semanticContext:null }; 24 | if(isCfg) { 25 | result.reachesIntoOuterContext = 0; 26 | } 27 | return result; 28 | } else { 29 | var props = {}; 30 | props.state = params.state || null; 31 | props.alt = (params.alt === undefined) ? null : params.alt; 32 | props.context = params.context || null; 33 | props.semanticContext = params.semanticContext || null; 34 | if(isCfg) { 35 | props.reachesIntoOuterContext = params.reachesIntoOuterContext || 0; 36 | props.precedenceFilterSuppressed = params.precedenceFilterSuppressed || false; 37 | } 38 | return props; 39 | } 40 | } 41 | 42 | function ATNConfig(params, config) { 43 | this.checkContext(params, config); 44 | params = checkParams(params); 45 | config = checkParams(config, true); 46 | // The ATN state associated with this configuration/// 47 | this.state = params.state!==null ? params.state : config.state; 48 | // What alt (or lexer rule) is predicted by this configuration/// 49 | this.alt = params.alt!==null ? params.alt : config.alt; 50 | // The stack of invoking states leading to the rule/states associated 51 | // with this config. We track only those contexts pushed during 52 | // execution of the ATN simulator. 53 | this.context = params.context!==null ? params.context : config.context; 54 | this.semanticContext = params.semanticContext!==null ? params.semanticContext : 55 | (config.semanticContext!==null ? config.semanticContext : SemanticContext.NONE); 56 | // We cannot execute predicates dependent upon local context unless 57 | // we know for sure we are in the correct context. Because there is 58 | // no way to do this efficiently, we simply cannot evaluate 59 | // dependent predicates unless we are in the rule that initially 60 | // invokes the ATN simulator. 61 | // 62 | // closure() tracks the depth of how far we dip into the 63 | // outer context: depth > 0. Note that it may not be totally 64 | // accurate depth since I don't ever decrement. TODO: make it a boolean then 65 | this.reachesIntoOuterContext = config.reachesIntoOuterContext; 66 | this.precedenceFilterSuppressed = config.precedenceFilterSuppressed; 67 | return this; 68 | } 69 | 70 | ATNConfig.prototype.checkContext = function(params, config) { 71 | if((params.context===null || params.context===undefined) && 72 | (config===null || config.context===null || config.context===undefined)) { 73 | this.context = null; 74 | } 75 | }; 76 | 77 | 78 | ATNConfig.prototype.hashCode = function() { 79 | var hash = new Hash(); 80 | this.updateHashCode(hash); 81 | return hash.finish(); 82 | }; 83 | 84 | 85 | ATNConfig.prototype.updateHashCode = function(hash) { 86 | hash.update(this.state.stateNumber, this.alt, this.context, this.semanticContext); 87 | }; 88 | 89 | // An ATN configuration is equal to another if both have 90 | // the same state, they predict the same alternative, and 91 | // syntactic/semantic contexts are the same. 92 | 93 | ATNConfig.prototype.equals = function(other) { 94 | if (this === other) { 95 | return true; 96 | } else if (! (other instanceof ATNConfig)) { 97 | return false; 98 | } else { 99 | return this.state.stateNumber===other.state.stateNumber && 100 | this.alt===other.alt && 101 | (this.context===null ? other.context===null : this.context.equals(other.context)) && 102 | this.semanticContext.equals(other.semanticContext) && 103 | this.precedenceFilterSuppressed===other.precedenceFilterSuppressed; 104 | } 105 | }; 106 | 107 | 108 | ATNConfig.prototype.hashCodeForConfigSet = function() { 109 | var hash = new Hash(); 110 | hash.update(this.state.stateNumber, this.alt, this.semanticContext); 111 | return hash.finish(); 112 | }; 113 | 114 | 115 | ATNConfig.prototype.equalsForConfigSet = function(other) { 116 | if (this === other) { 117 | return true; 118 | } else if (! (other instanceof ATNConfig)) { 119 | return false; 120 | } else { 121 | return this.state.stateNumber===other.state.stateNumber && 122 | this.alt===other.alt && 123 | this.semanticContext.equals(other.semanticContext); 124 | } 125 | }; 126 | 127 | 128 | ATNConfig.prototype.toString = function() { 129 | return "(" + this.state + "," + this.alt + 130 | (this.context!==null ? ",[" + this.context.toString() + "]" : "") + 131 | (this.semanticContext !== SemanticContext.NONE ? 132 | ("," + this.semanticContext.toString()) 133 | : "") + 134 | (this.reachesIntoOuterContext>0 ? 135 | (",up=" + this.reachesIntoOuterContext) 136 | : "") + ")"; 137 | }; 138 | 139 | 140 | function LexerATNConfig(params, config) { 141 | ATNConfig.call(this, params, config); 142 | 143 | // This is the backing field for {@link //getLexerActionExecutor}. 144 | var lexerActionExecutor = params.lexerActionExecutor || null; 145 | this.lexerActionExecutor = lexerActionExecutor || (config!==null ? config.lexerActionExecutor : null); 146 | this.passedThroughNonGreedyDecision = config!==null ? this.checkNonGreedyDecision(config, this.state) : false; 147 | return this; 148 | } 149 | 150 | LexerATNConfig.prototype = Object.create(ATNConfig.prototype); 151 | LexerATNConfig.prototype.constructor = LexerATNConfig; 152 | 153 | LexerATNConfig.prototype.updateHashCode = function(hash) { 154 | hash.update(this.state.stateNumber, this.alt, this.context, this.semanticContext, this.passedThroughNonGreedyDecision, this.lexerActionExecutor); 155 | }; 156 | 157 | LexerATNConfig.prototype.equals = function(other) { 158 | return this === other || 159 | (other instanceof LexerATNConfig && 160 | this.passedThroughNonGreedyDecision == other.passedThroughNonGreedyDecision && 161 | (this.lexerActionExecutor ? this.lexerActionExecutor.equals(other.lexerActionExecutor) : !other.lexerActionExecutor) && 162 | ATNConfig.prototype.equals.call(this, other)); 163 | }; 164 | 165 | LexerATNConfig.prototype.hashCodeForConfigSet = LexerATNConfig.prototype.hashCode; 166 | 167 | LexerATNConfig.prototype.equalsForConfigSet = LexerATNConfig.prototype.equals; 168 | 169 | 170 | LexerATNConfig.prototype.checkNonGreedyDecision = function(source, target) { 171 | return source.passedThroughNonGreedyDecision || 172 | (target instanceof DecisionState) && target.nonGreedy; 173 | }; 174 | 175 | exports.ATNConfig = ATNConfig; 176 | exports.LexerATNConfig = LexerATNConfig; -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/ATNDeserializationOptions.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | function ATNDeserializationOptions(copyFrom) { 7 | if(copyFrom===undefined) { 8 | copyFrom = null; 9 | } 10 | this.readOnly = false; 11 | this.verifyATN = copyFrom===null ? true : copyFrom.verifyATN; 12 | this.generateRuleBypassTransitions = copyFrom===null ? false : copyFrom.generateRuleBypassTransitions; 13 | 14 | return this; 15 | } 16 | 17 | ATNDeserializationOptions.defaultOptions = new ATNDeserializationOptions(); 18 | ATNDeserializationOptions.defaultOptions.readOnly = true; 19 | 20 | // def __setattr__(self, key, value): 21 | // if key!="readOnly" and self.readOnly: 22 | // raise Exception("The object is read only.") 23 | // super(type(self), self).__setattr__(key,value) 24 | 25 | exports.ATNDeserializationOptions = ATNDeserializationOptions; 26 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/ATNSimulator.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | var DFAState = require('./../dfa/DFAState').DFAState; 9 | var ATNConfigSet = require('./ATNConfigSet').ATNConfigSet; 10 | var getCachedPredictionContext = require('./../PredictionContext').getCachedPredictionContext; 11 | 12 | function ATNSimulator(atn, sharedContextCache) { 13 | 14 | // The context cache maps all PredictionContext objects that are == 15 | // to a single cached copy. This cache is shared across all contexts 16 | // in all ATNConfigs in all DFA states. We rebuild each ATNConfigSet 17 | // to use only cached nodes/graphs in addDFAState(). We don't want to 18 | // fill this during closure() since there are lots of contexts that 19 | // pop up but are not used ever again. It also greatly slows down closure(). 20 | // 21 | //

This cache makes a huge difference in memory and a little bit in speed. 22 | // For the Java grammar on java.*, it dropped the memory requirements 23 | // at the end from 25M to 16M. We don't store any of the full context 24 | // graphs in the DFA because they are limited to local context only, 25 | // but apparently there's a lot of repetition there as well. We optimize 26 | // the config contexts before storing the config set in the DFA states 27 | // by literally rebuilding them with cached subgraphs only.

28 | // 29 | //

I tried a cache for use during closure operations, that was 30 | // whacked after each adaptivePredict(). It cost a little bit 31 | // more time I think and doesn't save on the overall footprint 32 | // so it's not worth the complexity.

33 | /// 34 | this.atn = atn; 35 | this.sharedContextCache = sharedContextCache; 36 | return this; 37 | } 38 | 39 | // Must distinguish between missing edge and edge we know leads nowhere/// 40 | ATNSimulator.ERROR = new DFAState(0x7FFFFFFF, new ATNConfigSet()); 41 | 42 | 43 | ATNSimulator.prototype.getCachedContext = function(context) { 44 | if (this.sharedContextCache ===null) { 45 | return context; 46 | } 47 | var visited = {}; 48 | return getCachedPredictionContext(context, this.sharedContextCache, visited); 49 | }; 50 | 51 | exports.ATNSimulator = ATNSimulator; 52 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/ATNType.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | /// 6 | 7 | // Represents the type of recognizer an ATN applies to. 8 | 9 | function ATNType() { 10 | 11 | } 12 | 13 | ATNType.LEXER = 0; 14 | ATNType.PARSER = 1; 15 | 16 | exports.ATNType = ATNType; 17 | 18 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/LexerActionExecutor.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | // Represents an executor for a sequence of lexer actions which traversed during 9 | // the matching operation of a lexer rule (token). 10 | // 11 | //

The executor tracks position information for position-dependent lexer actions 12 | // efficiently, ensuring that actions appearing only at the end of the rule do 13 | // not cause bloating of the {@link DFA} created for the lexer.

14 | 15 | var hashStuff = require("../Utils").hashStuff; 16 | var LexerIndexedCustomAction = require('./LexerAction').LexerIndexedCustomAction; 17 | 18 | function LexerActionExecutor(lexerActions) { 19 | this.lexerActions = lexerActions === null ? [] : lexerActions; 20 | // Caches the result of {@link //hashCode} since the hash code is an element 21 | // of the performance-critical {@link LexerATNConfig//hashCode} operation. 22 | this.cachedHashCode = hashStuff(lexerActions); // "".join([str(la) for la in 23 | // lexerActions])) 24 | return this; 25 | } 26 | 27 | // Creates a {@link LexerActionExecutor} which executes the actions for 28 | // the input {@code lexerActionExecutor} followed by a specified 29 | // {@code lexerAction}. 30 | // 31 | // @param lexerActionExecutor The executor for actions already traversed by 32 | // the lexer while matching a token within a particular 33 | // {@link LexerATNConfig}. If this is {@code null}, the method behaves as 34 | // though it were an empty executor. 35 | // @param lexerAction The lexer action to execute after the actions 36 | // specified in {@code lexerActionExecutor}. 37 | // 38 | // @return A {@link LexerActionExecutor} for executing the combine actions 39 | // of {@code lexerActionExecutor} and {@code lexerAction}. 40 | LexerActionExecutor.append = function(lexerActionExecutor, lexerAction) { 41 | if (lexerActionExecutor === null) { 42 | return new LexerActionExecutor([ lexerAction ]); 43 | } 44 | var lexerActions = lexerActionExecutor.lexerActions.concat([ lexerAction ]); 45 | return new LexerActionExecutor(lexerActions); 46 | }; 47 | 48 | // Creates a {@link LexerActionExecutor} which encodes the current offset 49 | // for position-dependent lexer actions. 50 | // 51 | //

Normally, when the executor encounters lexer actions where 52 | // {@link LexerAction//isPositionDependent} returns {@code true}, it calls 53 | // {@link IntStream//seek} on the input {@link CharStream} to set the input 54 | // position to the end of the current token. This behavior provides 55 | // for efficient DFA representation of lexer actions which appear at the end 56 | // of a lexer rule, even when the lexer rule matches a variable number of 57 | // characters.

58 | // 59 | //

Prior to traversing a match transition in the ATN, the current offset 60 | // from the token start index is assigned to all position-dependent lexer 61 | // actions which have not already been assigned a fixed offset. By storing 62 | // the offsets relative to the token start index, the DFA representation of 63 | // lexer actions which appear in the middle of tokens remains efficient due 64 | // to sharing among tokens of the same length, regardless of their absolute 65 | // position in the input stream.

66 | // 67 | //

If the current executor already has offsets assigned to all 68 | // position-dependent lexer actions, the method returns {@code this}.

69 | // 70 | // @param offset The current offset to assign to all position-dependent 71 | // lexer actions which do not already have offsets assigned. 72 | // 73 | // @return A {@link LexerActionExecutor} which stores input stream offsets 74 | // for all position-dependent lexer actions. 75 | // / 76 | LexerActionExecutor.prototype.fixOffsetBeforeMatch = function(offset) { 77 | var updatedLexerActions = null; 78 | for (var i = 0; i < this.lexerActions.length; i++) { 79 | if (this.lexerActions[i].isPositionDependent && 80 | !(this.lexerActions[i] instanceof LexerIndexedCustomAction)) { 81 | if (updatedLexerActions === null) { 82 | updatedLexerActions = this.lexerActions.concat([]); 83 | } 84 | updatedLexerActions[i] = new LexerIndexedCustomAction(offset, 85 | this.lexerActions[i]); 86 | } 87 | } 88 | if (updatedLexerActions === null) { 89 | return this; 90 | } else { 91 | return new LexerActionExecutor(updatedLexerActions); 92 | } 93 | }; 94 | 95 | // Execute the actions encapsulated by this executor within the context of a 96 | // particular {@link Lexer}. 97 | // 98 | //

This method calls {@link IntStream//seek} to set the position of the 99 | // {@code input} {@link CharStream} prior to calling 100 | // {@link LexerAction//execute} on a position-dependent action. Before the 101 | // method returns, the input position will be restored to the same position 102 | // it was in when the method was invoked.

103 | // 104 | // @param lexer The lexer instance. 105 | // @param input The input stream which is the source for the current token. 106 | // When this method is called, the current {@link IntStream//index} for 107 | // {@code input} should be the start of the following token, i.e. 1 108 | // character past the end of the current token. 109 | // @param startIndex The token start index. This value may be passed to 110 | // {@link IntStream//seek} to set the {@code input} position to the beginning 111 | // of the token. 112 | // / 113 | LexerActionExecutor.prototype.execute = function(lexer, input, startIndex) { 114 | var requiresSeek = false; 115 | var stopIndex = input.index; 116 | try { 117 | for (var i = 0; i < this.lexerActions.length; i++) { 118 | var lexerAction = this.lexerActions[i]; 119 | if (lexerAction instanceof LexerIndexedCustomAction) { 120 | var offset = lexerAction.offset; 121 | input.seek(startIndex + offset); 122 | lexerAction = lexerAction.action; 123 | requiresSeek = (startIndex + offset) !== stopIndex; 124 | } else if (lexerAction.isPositionDependent) { 125 | input.seek(stopIndex); 126 | requiresSeek = false; 127 | } 128 | lexerAction.execute(lexer); 129 | } 130 | } finally { 131 | if (requiresSeek) { 132 | input.seek(stopIndex); 133 | } 134 | } 135 | }; 136 | 137 | LexerActionExecutor.prototype.hashCode = function() { 138 | return this.cachedHashCode; 139 | }; 140 | 141 | LexerActionExecutor.prototype.updateHashCode = function(hash) { 142 | hash.update(this.cachedHashCode); 143 | }; 144 | 145 | 146 | LexerActionExecutor.prototype.equals = function(other) { 147 | if (this === other) { 148 | return true; 149 | } else if (!(other instanceof LexerActionExecutor)) { 150 | return false; 151 | } else if (this.cachedHashCode != other.cachedHashCode) { 152 | return false; 153 | } else if (this.lexerActions.length != other.lexerActions.length) { 154 | return false; 155 | } else { 156 | var numActions = this.lexerActions.length 157 | for (var idx = 0; idx < numActions; ++idx) { 158 | if (!this.lexerActions[idx].equals(other.lexerActions[idx])) { 159 | return false; 160 | } 161 | } 162 | return true; 163 | } 164 | }; 165 | 166 | exports.LexerActionExecutor = LexerActionExecutor; 167 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/atn/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | exports.ATN = require('./ATN').ATN; 7 | exports.ATNDeserializer = require('./ATNDeserializer').ATNDeserializer; 8 | exports.LexerATNSimulator = require('./LexerATNSimulator').LexerATNSimulator; 9 | exports.ParserATNSimulator = require('./ParserATNSimulator').ParserATNSimulator; 10 | exports.PredictionMode = require('./PredictionMode').PredictionMode; 11 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/dfa/DFA.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | var Set = require("../Utils").Set; 8 | var DFAState = require('./DFAState').DFAState; 9 | var StarLoopEntryState = require('../atn/ATNState').StarLoopEntryState; 10 | var ATNConfigSet = require('./../atn/ATNConfigSet').ATNConfigSet; 11 | var DFASerializer = require('./DFASerializer').DFASerializer; 12 | var LexerDFASerializer = require('./DFASerializer').LexerDFASerializer; 13 | 14 | 15 | 16 | function DFA(atnStartState, decision) { 17 | if (decision === undefined) { 18 | decision = 0; 19 | } 20 | // From which ATN state did we create this DFA? 21 | this.atnStartState = atnStartState; 22 | this.decision = decision; 23 | // A set of all DFA states. Use {@link Map} so we can get old state back 24 | // ({@link Set} only allows you to see if it's there). 25 | this._states = new Set(); 26 | this.s0 = null; 27 | // {@code true} if this DFA is for a precedence decision; otherwise, 28 | // {@code false}. This is the backing field for {@link //isPrecedenceDfa}, 29 | // {@link //setPrecedenceDfa}. 30 | this.precedenceDfa = false; 31 | if (atnStartState instanceof StarLoopEntryState) 32 | { 33 | if (atnStartState.isPrecedenceDecision) { 34 | this.precedenceDfa = true; 35 | var precedenceState = new DFAState(null, new ATNConfigSet()); 36 | precedenceState.edges = []; 37 | precedenceState.isAcceptState = false; 38 | precedenceState.requiresFullContext = false; 39 | this.s0 = precedenceState; 40 | } 41 | } 42 | return this; 43 | } 44 | 45 | // Get the start state for a specific precedence value. 46 | // 47 | // @param precedence The current precedence. 48 | // @return The start state corresponding to the specified precedence, or 49 | // {@code null} if no start state exists for the specified precedence. 50 | // 51 | // @throws IllegalStateException if this is not a precedence DFA. 52 | // @see //isPrecedenceDfa() 53 | 54 | DFA.prototype.getPrecedenceStartState = function(precedence) { 55 | if (!(this.precedenceDfa)) { 56 | throw ("Only precedence DFAs may contain a precedence start state."); 57 | } 58 | // s0.edges is never null for a precedence DFA 59 | if (precedence < 0 || precedence >= this.s0.edges.length) { 60 | return null; 61 | } 62 | return this.s0.edges[precedence] || null; 63 | }; 64 | 65 | // Set the start state for a specific precedence value. 66 | // 67 | // @param precedence The current precedence. 68 | // @param startState The start state corresponding to the specified 69 | // precedence. 70 | // 71 | // @throws IllegalStateException if this is not a precedence DFA. 72 | // @see //isPrecedenceDfa() 73 | // 74 | DFA.prototype.setPrecedenceStartState = function(precedence, startState) { 75 | if (!(this.precedenceDfa)) { 76 | throw ("Only precedence DFAs may contain a precedence start state."); 77 | } 78 | if (precedence < 0) { 79 | return; 80 | } 81 | 82 | // synchronization on s0 here is ok. when the DFA is turned into a 83 | // precedence DFA, s0 will be initialized once and not updated again 84 | // s0.edges is never null for a precedence DFA 85 | this.s0.edges[precedence] = startState; 86 | }; 87 | 88 | // 89 | // Sets whether this is a precedence DFA. If the specified value differs 90 | // from the current DFA configuration, the following actions are taken; 91 | // otherwise no changes are made to the current DFA. 92 | // 93 | //
    94 | //
  • The {@link //states} map is cleared
  • 95 | //
  • If {@code precedenceDfa} is {@code false}, the initial state 96 | // {@link //s0} is set to {@code null}; otherwise, it is initialized to a new 97 | // {@link DFAState} with an empty outgoing {@link DFAState//edges} array to 98 | // store the start states for individual precedence values.
  • 99 | //
  • The {@link //precedenceDfa} field is updated
  • 100 | //
101 | // 102 | // @param precedenceDfa {@code true} if this is a precedence DFA; otherwise, 103 | // {@code false} 104 | 105 | DFA.prototype.setPrecedenceDfa = function(precedenceDfa) { 106 | if (this.precedenceDfa!==precedenceDfa) { 107 | this._states = new DFAStatesSet(); 108 | if (precedenceDfa) { 109 | var precedenceState = new DFAState(null, new ATNConfigSet()); 110 | precedenceState.edges = []; 111 | precedenceState.isAcceptState = false; 112 | precedenceState.requiresFullContext = false; 113 | this.s0 = precedenceState; 114 | } else { 115 | this.s0 = null; 116 | } 117 | this.precedenceDfa = precedenceDfa; 118 | } 119 | }; 120 | 121 | Object.defineProperty(DFA.prototype, "states", { 122 | get : function() { 123 | return this._states; 124 | } 125 | }); 126 | 127 | // Return a list of all states in this DFA, ordered by state number. 128 | DFA.prototype.sortedStates = function() { 129 | var list = this._states.values(); 130 | return list.sort(function(a, b) { 131 | return a.stateNumber - b.stateNumber; 132 | }); 133 | }; 134 | 135 | DFA.prototype.toString = function(literalNames, symbolicNames) { 136 | literalNames = literalNames || null; 137 | symbolicNames = symbolicNames || null; 138 | if (this.s0 === null) { 139 | return ""; 140 | } 141 | var serializer = new DFASerializer(this, literalNames, symbolicNames); 142 | return serializer.toString(); 143 | }; 144 | 145 | DFA.prototype.toLexerString = function() { 146 | if (this.s0 === null) { 147 | return ""; 148 | } 149 | var serializer = new LexerDFASerializer(this); 150 | return serializer.toString(); 151 | }; 152 | 153 | exports.DFA = DFA; 154 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/dfa/DFASerializer.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | // A DFA walker that knows how to dump them to serialized strings.#/ 7 | 8 | 9 | function DFASerializer(dfa, literalNames, symbolicNames) { 10 | this.dfa = dfa; 11 | this.literalNames = literalNames || []; 12 | this.symbolicNames = symbolicNames || []; 13 | return this; 14 | } 15 | 16 | DFASerializer.prototype.toString = function() { 17 | if(this.dfa.s0 === null) { 18 | return null; 19 | } 20 | var buf = ""; 21 | var states = this.dfa.sortedStates(); 22 | for(var i=0;i"); 33 | buf = buf.concat(this.getStateString(t)); 34 | buf = buf.concat('\n'); 35 | } 36 | } 37 | } 38 | } 39 | return buf.length===0 ? null : buf; 40 | }; 41 | 42 | DFASerializer.prototype.getEdgeLabel = function(i) { 43 | if (i===0) { 44 | return "EOF"; 45 | } else if(this.literalNames !==null || this.symbolicNames!==null) { 46 | return this.literalNames[i-1] || this.symbolicNames[i-1]; 47 | } else { 48 | return String.fromCharCode(i-1); 49 | } 50 | }; 51 | 52 | DFASerializer.prototype.getStateString = function(s) { 53 | var baseStateStr = ( s.isAcceptState ? ":" : "") + "s" + s.stateNumber + ( s.requiresFullContext ? "^" : ""); 54 | if(s.isAcceptState) { 55 | if (s.predicates !== null) { 56 | return baseStateStr + "=>" + s.predicates.toString(); 57 | } else { 58 | return baseStateStr + "=>" + s.prediction.toString(); 59 | } 60 | } else { 61 | return baseStateStr; 62 | } 63 | }; 64 | 65 | function LexerDFASerializer(dfa) { 66 | DFASerializer.call(this, dfa, null); 67 | return this; 68 | } 69 | 70 | LexerDFASerializer.prototype = Object.create(DFASerializer.prototype); 71 | LexerDFASerializer.prototype.constructor = LexerDFASerializer; 72 | 73 | LexerDFASerializer.prototype.getEdgeLabel = function(i) { 74 | return "'" + String.fromCharCode(i) + "'"; 75 | }; 76 | 77 | exports.DFASerializer = DFASerializer; 78 | exports.LexerDFASerializer = LexerDFASerializer; 79 | 80 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/dfa/DFAState.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | /// 7 | 8 | var ATNConfigSet = require('./../atn/ATNConfigSet').ATNConfigSet; 9 | var Utils = require('./../Utils'); 10 | var Hash = Utils.Hash; 11 | var Set = Utils.Set; 12 | 13 | // Map a predicate to a predicted alternative./// 14 | 15 | function PredPrediction(pred, alt) { 16 | this.alt = alt; 17 | this.pred = pred; 18 | return this; 19 | } 20 | 21 | PredPrediction.prototype.toString = function() { 22 | return "(" + this.pred + ", " + this.alt + ")"; 23 | }; 24 | 25 | // A DFA state represents a set of possible ATN configurations. 26 | // As Aho, Sethi, Ullman p. 117 says "The DFA uses its state 27 | // to keep track of all possible states the ATN can be in after 28 | // reading each input symbol. That is to say, after reading 29 | // input a1a2..an, the DFA is in a state that represents the 30 | // subset T of the states of the ATN that are reachable from the 31 | // ATN's start state along some path labeled a1a2..an." 32 | // In conventional NFA→DFA conversion, therefore, the subset T 33 | // would be a bitset representing the set of states the 34 | // ATN could be in. We need to track the alt predicted by each 35 | // state as well, however. More importantly, we need to maintain 36 | // a stack of states, tracking the closure operations as they 37 | // jump from rule to rule, emulating rule invocations (method calls). 38 | // I have to add a stack to simulate the proper lookahead sequences for 39 | // the underlying LL grammar from which the ATN was derived. 40 | // 41 | //

I use a set of ATNConfig objects not simple states. An ATNConfig 42 | // is both a state (ala normal conversion) and a RuleContext describing 43 | // the chain of rules (if any) followed to arrive at that state.

44 | // 45 | //

A DFA state may have multiple references to a particular state, 46 | // but with different ATN contexts (with same or different alts) 47 | // meaning that state was reached via a different set of rule invocations.

48 | // / 49 | 50 | function DFAState(stateNumber, configs) { 51 | if (stateNumber === null) { 52 | stateNumber = -1; 53 | } 54 | if (configs === null) { 55 | configs = new ATNConfigSet(); 56 | } 57 | this.stateNumber = stateNumber; 58 | this.configs = configs; 59 | // {@code edges[symbol]} points to target of symbol. Shift up by 1 so (-1) 60 | // {@link Token//EOF} maps to {@code edges[0]}. 61 | this.edges = null; 62 | this.isAcceptState = false; 63 | // if accept state, what ttype do we match or alt do we predict? 64 | // This is set to {@link ATN//INVALID_ALT_NUMBER} when {@link 65 | // //predicates}{@code !=null} or 66 | // {@link //requiresFullContext}. 67 | this.prediction = 0; 68 | this.lexerActionExecutor = null; 69 | // Indicates that this state was created during SLL prediction that 70 | // discovered a conflict between the configurations in the state. Future 71 | // {@link ParserATNSimulator//execATN} invocations immediately jumped doing 72 | // full context prediction if this field is true. 73 | this.requiresFullContext = false; 74 | // During SLL parsing, this is a list of predicates associated with the 75 | // ATN configurations of the DFA state. When we have predicates, 76 | // {@link //requiresFullContext} is {@code false} since full context 77 | // prediction evaluates predicates 78 | // on-the-fly. If this is not null, then {@link //prediction} is 79 | // {@link ATN//INVALID_ALT_NUMBER}. 80 | // 81 | //

We only use these for non-{@link //requiresFullContext} but 82 | // conflicting states. That 83 | // means we know from the context (it's $ or we don't dip into outer 84 | // context) that it's an ambiguity not a conflict.

85 | // 86 | //

This list is computed by {@link 87 | // ParserATNSimulator//predicateDFAState}.

88 | this.predicates = null; 89 | return this; 90 | } 91 | 92 | // Get the set of all alts mentioned by all ATN configurations in this 93 | // DFA state. 94 | DFAState.prototype.getAltSet = function() { 95 | var alts = new Set(); 96 | if (this.configs !== null) { 97 | for (var i = 0; i < this.configs.length; i++) { 98 | var c = this.configs[i]; 99 | alts.add(c.alt); 100 | } 101 | } 102 | if (alts.length === 0) { 103 | return null; 104 | } else { 105 | return alts; 106 | } 107 | }; 108 | 109 | // Two {@link DFAState} instances are equal if their ATN configuration sets 110 | // are the same. This method is used to see if a state already exists. 111 | // 112 | //

Because the number of alternatives and number of ATN configurations are 113 | // finite, there is a finite number of DFA states that can be processed. 114 | // This is necessary to show that the algorithm terminates.

115 | // 116 | //

Cannot test the DFA state numbers here because in 117 | // {@link ParserATNSimulator//addDFAState} we need to know if any other state 118 | // exists that has this exact set of ATN configurations. The 119 | // {@link //stateNumber} is irrelevant.

120 | DFAState.prototype.equals = function(other) { 121 | // compare set of ATN configurations in this set with other 122 | return this === other || 123 | (other instanceof DFAState && 124 | this.configs.equals(other.configs)); 125 | }; 126 | 127 | DFAState.prototype.toString = function() { 128 | var s = "" + this.stateNumber + ":" + this.configs; 129 | if(this.isAcceptState) { 130 | s = s + "=>"; 131 | if (this.predicates !== null) 132 | s = s + this.predicates; 133 | else 134 | s = s + this.prediction; 135 | } 136 | return s; 137 | }; 138 | 139 | DFAState.prototype.hashCode = function() { 140 | var hash = new Hash(); 141 | hash.update(this.configs); 142 | if(this.isAcceptState) { 143 | if (this.predicates !== null) 144 | hash.update(this.predicates); 145 | else 146 | hash.update(this.prediction); 147 | } 148 | return hash.finish(); 149 | }; 150 | 151 | exports.DFAState = DFAState; 152 | exports.PredPrediction = PredPrediction; 153 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/dfa/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | exports.DFA = require('./DFA').DFA; 7 | exports.DFASerializer = require('./DFASerializer').DFASerializer; 8 | exports.LexerDFASerializer = require('./DFASerializer').LexerDFASerializer; 9 | exports.PredPrediction = require('./DFAState').PredPrediction; 10 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/error/DiagnosticErrorListener.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | // 7 | 8 | // 9 | // This implementation of {@link ANTLRErrorListener} can be used to identify 10 | // certain potential correctness and performance problems in grammars. "Reports" 11 | // are made by calling {@link Parser//notifyErrorListeners} with the appropriate 12 | // message. 13 | // 14 | //
    15 | //
  • Ambiguities: These are cases where more than one path through the 16 | // grammar can match the input.
  • 17 | //
  • Weak context sensitivity: These are cases where full-context 18 | // prediction resolved an SLL conflict to a unique alternative which equaled the 19 | // minimum alternative of the SLL conflict.
  • 20 | //
  • Strong (forced) context sensitivity: These are cases where the 21 | // full-context prediction resolved an SLL conflict to a unique alternative, 22 | // and the minimum alternative of the SLL conflict was found to not be 23 | // a truly viable alternative. Two-stage parsing cannot be used for inputs where 24 | // this situation occurs.
  • 25 | //
26 | 27 | var BitSet = require('./../Utils').BitSet; 28 | var ErrorListener = require('./ErrorListener').ErrorListener; 29 | var Interval = require('./../IntervalSet').Interval; 30 | 31 | function DiagnosticErrorListener(exactOnly) { 32 | ErrorListener.call(this); 33 | exactOnly = exactOnly || true; 34 | // whether all ambiguities or only exact ambiguities are reported. 35 | this.exactOnly = exactOnly; 36 | return this; 37 | } 38 | 39 | DiagnosticErrorListener.prototype = Object.create(ErrorListener.prototype); 40 | DiagnosticErrorListener.prototype.constructor = DiagnosticErrorListener; 41 | 42 | DiagnosticErrorListener.prototype.reportAmbiguity = function(recognizer, dfa, 43 | startIndex, stopIndex, exact, ambigAlts, configs) { 44 | if (this.exactOnly && !exact) { 45 | return; 46 | } 47 | var msg = "reportAmbiguity d=" + 48 | this.getDecisionDescription(recognizer, dfa) + 49 | ": ambigAlts=" + 50 | this.getConflictingAlts(ambigAlts, configs) + 51 | ", input='" + 52 | recognizer.getTokenStream().getText(new Interval(startIndex, stopIndex)) + "'"; 53 | recognizer.notifyErrorListeners(msg); 54 | }; 55 | 56 | DiagnosticErrorListener.prototype.reportAttemptingFullContext = function( 57 | recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) { 58 | var msg = "reportAttemptingFullContext d=" + 59 | this.getDecisionDescription(recognizer, dfa) + 60 | ", input='" + 61 | recognizer.getTokenStream().getText(new Interval(startIndex, stopIndex)) + "'"; 62 | recognizer.notifyErrorListeners(msg); 63 | }; 64 | 65 | DiagnosticErrorListener.prototype.reportContextSensitivity = function( 66 | recognizer, dfa, startIndex, stopIndex, prediction, configs) { 67 | var msg = "reportContextSensitivity d=" + 68 | this.getDecisionDescription(recognizer, dfa) + 69 | ", input='" + 70 | recognizer.getTokenStream().getText(new Interval(startIndex, stopIndex)) + "'"; 71 | recognizer.notifyErrorListeners(msg); 72 | }; 73 | 74 | DiagnosticErrorListener.prototype.getDecisionDescription = function(recognizer, dfa) { 75 | var decision = dfa.decision; 76 | var ruleIndex = dfa.atnStartState.ruleIndex; 77 | 78 | var ruleNames = recognizer.ruleNames; 79 | if (ruleIndex < 0 || ruleIndex >= ruleNames.length) { 80 | return "" + decision; 81 | } 82 | var ruleName = ruleNames[ruleIndex] || null; 83 | if (ruleName === null || ruleName.length === 0) { 84 | return "" + decision; 85 | } 86 | return "" + decision + " (" + ruleName + ")"; 87 | }; 88 | 89 | // 90 | // Computes the set of conflicting or ambiguous alternatives from a 91 | // configuration set, if that information was not already provided by the 92 | // parser. 93 | // 94 | // @param reportedAlts The set of conflicting or ambiguous alternatives, as 95 | // reported by the parser. 96 | // @param configs The conflicting or ambiguous configuration set. 97 | // @return Returns {@code reportedAlts} if it is not {@code null}, otherwise 98 | // returns the set of alternatives represented in {@code configs}. 99 | // 100 | DiagnosticErrorListener.prototype.getConflictingAlts = function(reportedAlts, configs) { 101 | if (reportedAlts !== null) { 102 | return reportedAlts; 103 | } 104 | var result = new BitSet(); 105 | for (var i = 0; i < configs.items.length; i++) { 106 | result.add(configs.items[i].alt); 107 | } 108 | return "{" + result.values().join(", ") + "}"; 109 | }; 110 | 111 | exports.DiagnosticErrorListener = DiagnosticErrorListener; -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/error/ErrorListener.js: -------------------------------------------------------------------------------- 1 | // 2 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | // Provides an empty default implementation of {@link ANTLRErrorListener}. The 8 | // default implementation of each method does nothing, but can be overridden as 9 | // necessary. 10 | 11 | function ErrorListener() { 12 | return this; 13 | } 14 | 15 | ErrorListener.prototype.syntaxError = function(recognizer, offendingSymbol, line, column, msg, e) { 16 | }; 17 | 18 | ErrorListener.prototype.reportAmbiguity = function(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs) { 19 | }; 20 | 21 | ErrorListener.prototype.reportAttemptingFullContext = function(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) { 22 | }; 23 | 24 | ErrorListener.prototype.reportContextSensitivity = function(recognizer, dfa, startIndex, stopIndex, prediction, configs) { 25 | }; 26 | 27 | function ConsoleErrorListener() { 28 | ErrorListener.call(this); 29 | return this; 30 | } 31 | 32 | ConsoleErrorListener.prototype = Object.create(ErrorListener.prototype); 33 | ConsoleErrorListener.prototype.constructor = ConsoleErrorListener; 34 | 35 | // 36 | // Provides a default instance of {@link ConsoleErrorListener}. 37 | // 38 | ConsoleErrorListener.INSTANCE = new ConsoleErrorListener(); 39 | 40 | // 41 | // {@inheritDoc} 42 | // 43 | //

44 | // This implementation prints messages to {@link System//err} containing the 45 | // values of {@code line}, {@code charPositionInLine}, and {@code msg} using 46 | // the following format.

47 | // 48 | //
49 | // line line:charPositionInLine msg
50 | // 
51 | // 52 | ConsoleErrorListener.prototype.syntaxError = function(recognizer, offendingSymbol, line, column, msg, e) { 53 | console.error("line " + line + ":" + column + " " + msg); 54 | }; 55 | 56 | function ProxyErrorListener(delegates) { 57 | ErrorListener.call(this); 58 | if (delegates===null) { 59 | throw "delegates"; 60 | } 61 | this.delegates = delegates; 62 | return this; 63 | } 64 | 65 | ProxyErrorListener.prototype = Object.create(ErrorListener.prototype); 66 | ProxyErrorListener.prototype.constructor = ProxyErrorListener; 67 | 68 | ProxyErrorListener.prototype.syntaxError = function(recognizer, offendingSymbol, line, column, msg, e) { 69 | this.delegates.map(function(d) { d.syntaxError(recognizer, offendingSymbol, line, column, msg, e); }); 70 | }; 71 | 72 | ProxyErrorListener.prototype.reportAmbiguity = function(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs) { 73 | this.delegates.map(function(d) { d.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs); }); 74 | }; 75 | 76 | ProxyErrorListener.prototype.reportAttemptingFullContext = function(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) { 77 | this.delegates.map(function(d) { d.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs); }); 78 | }; 79 | 80 | ProxyErrorListener.prototype.reportContextSensitivity = function(recognizer, dfa, startIndex, stopIndex, prediction, configs) { 81 | this.delegates.map(function(d) { d.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs); }); 82 | }; 83 | 84 | exports.ErrorListener = ErrorListener; 85 | exports.ConsoleErrorListener = ConsoleErrorListener; 86 | exports.ProxyErrorListener = ProxyErrorListener; 87 | 88 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/error/Errors.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | // The root of the ANTLR exception hierarchy. In general, ANTLR tracks just 7 | // 3 kinds of errors: prediction errors, failed predicate errors, and 8 | // mismatched input errors. In each case, the parser knows where it is 9 | // in the input, where it is in the ATN, the rule invocation stack, 10 | // and what kind of problem occurred. 11 | 12 | var PredicateTransition = require('./../atn/Transition').PredicateTransition; 13 | 14 | function RecognitionException(params) { 15 | Error.call(this); 16 | if (!!Error.captureStackTrace) { 17 | Error.captureStackTrace(this, RecognitionException); 18 | } else { 19 | var stack = new Error().stack; 20 | } 21 | this.message = params.message; 22 | this.recognizer = params.recognizer; 23 | this.input = params.input; 24 | this.ctx = params.ctx; 25 | // The current {@link Token} when an error occurred. Since not all streams 26 | // support accessing symbols by index, we have to track the {@link Token} 27 | // instance itself. 28 | this.offendingToken = null; 29 | // Get the ATN state number the parser was in at the time the error 30 | // occurred. For {@link NoViableAltException} and 31 | // {@link LexerNoViableAltException} exceptions, this is the 32 | // {@link DecisionState} number. For others, it is the state whose outgoing 33 | // edge we couldn't match. 34 | this.offendingState = -1; 35 | if (this.recognizer!==null) { 36 | this.offendingState = this.recognizer.state; 37 | } 38 | return this; 39 | } 40 | 41 | RecognitionException.prototype = Object.create(Error.prototype); 42 | RecognitionException.prototype.constructor = RecognitionException; 43 | 44 | //

If the state number is not known, this method returns -1.

45 | 46 | // 47 | // Gets the set of input symbols which could potentially follow the 48 | // previously matched symbol at the time this exception was thrown. 49 | // 50 | //

If the set of expected tokens is not known and could not be computed, 51 | // this method returns {@code null}.

52 | // 53 | // @return The set of token types that could potentially follow the current 54 | // state in the ATN, or {@code null} if the information is not available. 55 | // / 56 | RecognitionException.prototype.getExpectedTokens = function() { 57 | if (this.recognizer!==null) { 58 | return this.recognizer.atn.getExpectedTokens(this.offendingState, this.ctx); 59 | } else { 60 | return null; 61 | } 62 | }; 63 | 64 | RecognitionException.prototype.toString = function() { 65 | return this.message; 66 | }; 67 | 68 | function LexerNoViableAltException(lexer, input, startIndex, deadEndConfigs) { 69 | RecognitionException.call(this, {message:"", recognizer:lexer, input:input, ctx:null}); 70 | this.startIndex = startIndex; 71 | this.deadEndConfigs = deadEndConfigs; 72 | return this; 73 | } 74 | 75 | LexerNoViableAltException.prototype = Object.create(RecognitionException.prototype); 76 | LexerNoViableAltException.prototype.constructor = LexerNoViableAltException; 77 | 78 | LexerNoViableAltException.prototype.toString = function() { 79 | var symbol = ""; 80 | if (this.startIndex >= 0 && this.startIndex < this.input.size) { 81 | symbol = this.input.getText((this.startIndex,this.startIndex)); 82 | } 83 | return "LexerNoViableAltException" + symbol; 84 | }; 85 | 86 | // Indicates that the parser could not decide which of two or more paths 87 | // to take based upon the remaining input. It tracks the starting token 88 | // of the offending input and also knows where the parser was 89 | // in the various paths when the error. Reported by reportNoViableAlternative() 90 | // 91 | function NoViableAltException(recognizer, input, startToken, offendingToken, deadEndConfigs, ctx) { 92 | ctx = ctx || recognizer._ctx; 93 | offendingToken = offendingToken || recognizer.getCurrentToken(); 94 | startToken = startToken || recognizer.getCurrentToken(); 95 | input = input || recognizer.getInputStream(); 96 | RecognitionException.call(this, {message:"", recognizer:recognizer, input:input, ctx:ctx}); 97 | // Which configurations did we try at input.index() that couldn't match 98 | // input.LT(1)?// 99 | this.deadEndConfigs = deadEndConfigs; 100 | // The token object at the start index; the input stream might 101 | // not be buffering tokens so get a reference to it. (At the 102 | // time the error occurred, of course the stream needs to keep a 103 | // buffer all of the tokens but later we might not have access to those.) 104 | this.startToken = startToken; 105 | this.offendingToken = offendingToken; 106 | } 107 | 108 | NoViableAltException.prototype = Object.create(RecognitionException.prototype); 109 | NoViableAltException.prototype.constructor = NoViableAltException; 110 | 111 | // This signifies any kind of mismatched input exceptions such as 112 | // when the current input does not match the expected token. 113 | // 114 | function InputMismatchException(recognizer) { 115 | RecognitionException.call(this, {message:"", recognizer:recognizer, input:recognizer.getInputStream(), ctx:recognizer._ctx}); 116 | this.offendingToken = recognizer.getCurrentToken(); 117 | } 118 | 119 | InputMismatchException.prototype = Object.create(RecognitionException.prototype); 120 | InputMismatchException.prototype.constructor = InputMismatchException; 121 | 122 | // A semantic predicate failed during validation. Validation of predicates 123 | // occurs when normally parsing the alternative just like matching a token. 124 | // Disambiguating predicate evaluation occurs when we test a predicate during 125 | // prediction. 126 | 127 | function FailedPredicateException(recognizer, predicate, message) { 128 | RecognitionException.call(this, {message:this.formatMessage(predicate,message || null), recognizer:recognizer, 129 | input:recognizer.getInputStream(), ctx:recognizer._ctx}); 130 | var s = recognizer._interp.atn.states[recognizer.state]; 131 | var trans = s.transitions[0]; 132 | if (trans instanceof PredicateTransition) { 133 | this.ruleIndex = trans.ruleIndex; 134 | this.predicateIndex = trans.predIndex; 135 | } else { 136 | this.ruleIndex = 0; 137 | this.predicateIndex = 0; 138 | } 139 | this.predicate = predicate; 140 | this.offendingToken = recognizer.getCurrentToken(); 141 | return this; 142 | } 143 | 144 | FailedPredicateException.prototype = Object.create(RecognitionException.prototype); 145 | FailedPredicateException.prototype.constructor = FailedPredicateException; 146 | 147 | FailedPredicateException.prototype.formatMessage = function(predicate, message) { 148 | if (message !==null) { 149 | return message; 150 | } else { 151 | return "failed predicate: {" + predicate + "}?"; 152 | } 153 | }; 154 | 155 | function ParseCancellationException() { 156 | Error.call(this); 157 | Error.captureStackTrace(this, ParseCancellationException); 158 | return this; 159 | } 160 | 161 | ParseCancellationException.prototype = Object.create(Error.prototype); 162 | ParseCancellationException.prototype.constructor = ParseCancellationException; 163 | 164 | exports.RecognitionException = RecognitionException; 165 | exports.NoViableAltException = NoViableAltException; 166 | exports.LexerNoViableAltException = LexerNoViableAltException; 167 | exports.InputMismatchException = InputMismatchException; 168 | exports.FailedPredicateException = FailedPredicateException; 169 | exports.ParseCancellationException = ParseCancellationException; 170 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/error/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | exports.RecognitionException = require('./Errors').RecognitionException; 7 | exports.NoViableAltException = require('./Errors').NoViableAltException; 8 | exports.LexerNoViableAltException = require('./Errors').LexerNoViableAltException; 9 | exports.InputMismatchException = require('./Errors').InputMismatchException; 10 | exports.FailedPredicateException = require('./Errors').FailedPredicateException; 11 | exports.DiagnosticErrorListener = require('./DiagnosticErrorListener').DiagnosticErrorListener; 12 | exports.BailErrorStrategy = require('./ErrorStrategy').BailErrorStrategy; 13 | exports.ErrorListener = require('./ErrorListener').ErrorListener; 14 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | exports.atn = require('./atn/index'); 6 | exports.codepointat = require('./polyfills/codepointat'); 7 | exports.dfa = require('./dfa/index'); 8 | exports.fromcodepoint = require('./polyfills/fromcodepoint'); 9 | exports.tree = require('./tree/index'); 10 | exports.error = require('./error/index'); 11 | exports.Token = require('./Token').Token; 12 | exports.CharStreams = require('./CharStreams').CharStreams; 13 | exports.CommonToken = require('./Token').CommonToken; 14 | exports.InputStream = require('./InputStream').InputStream; 15 | exports.FileStream = require('./FileStream').FileStream; 16 | exports.CommonTokenStream = require('./CommonTokenStream').CommonTokenStream; 17 | exports.Lexer = require('./Lexer').Lexer; 18 | exports.Parser = require('./Parser').Parser; 19 | var pc = require('./PredictionContext'); 20 | exports.PredictionContextCache = pc.PredictionContextCache; 21 | exports.ParserRuleContext = require('./ParserRuleContext').ParserRuleContext; 22 | exports.Interval = require('./IntervalSet').Interval; 23 | exports.Utils = require('./Utils'); 24 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antlr4", 3 | "version": "4.7.0", 4 | "description": "JavaScript runtime for ANTLR4", 5 | "main": "src/antlr4/index.js", 6 | "repository": "antlr/antlr4.git", 7 | "keywords": [ 8 | "lexer", 9 | "parser", 10 | "antlr", 11 | "antlr4", 12 | "grammar" 13 | ], 14 | "license": "BSD", 15 | "bugs": { 16 | "url": "https://github.com/antlr/antlr4/issues" 17 | }, 18 | "homepage": "https://github.com/antlr/antlr4" 19 | } 20 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/polyfills/codepointat.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/codepointat v0.2.0 by @mathias */ 2 | if (!String.prototype.codePointAt) { 3 | (function() { 4 | 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` 5 | var defineProperty = (function() { 6 | // IE 8 only supports `Object.defineProperty` on DOM elements 7 | try { 8 | var object = {}; 9 | var $defineProperty = Object.defineProperty; 10 | var result = $defineProperty(object, object, object) && $defineProperty; 11 | } catch(error) {} 12 | return result; 13 | }()); 14 | var codePointAt = function(position) { 15 | if (this == null) { 16 | throw TypeError(); 17 | } 18 | var string = String(this); 19 | var size = string.length; 20 | // `ToInteger` 21 | var index = position ? Number(position) : 0; 22 | if (index != index) { // better `isNaN` 23 | index = 0; 24 | } 25 | // Account for out-of-bounds indices: 26 | if (index < 0 || index >= size) { 27 | return undefined; 28 | } 29 | // Get the first code unit 30 | var first = string.charCodeAt(index); 31 | var second; 32 | if ( // check if it’s the start of a surrogate pair 33 | first >= 0xD800 && first <= 0xDBFF && // high surrogate 34 | size > index + 1 // there is a next code unit 35 | ) { 36 | second = string.charCodeAt(index + 1); 37 | if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate 38 | // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae 39 | return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; 40 | } 41 | } 42 | return first; 43 | }; 44 | if (defineProperty) { 45 | defineProperty(String.prototype, 'codePointAt', { 46 | 'value': codePointAt, 47 | 'configurable': true, 48 | 'writable': true 49 | }); 50 | } else { 51 | String.prototype.codePointAt = codePointAt; 52 | } 53 | }()); 54 | } 55 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/polyfills/fromcodepoint.js: -------------------------------------------------------------------------------- 1 | /*! https://mths.be/fromcodepoint v0.2.1 by @mathias */ 2 | if (!String.fromCodePoint) { 3 | (function() { 4 | var defineProperty = (function() { 5 | // IE 8 only supports `Object.defineProperty` on DOM elements 6 | try { 7 | var object = {}; 8 | var $defineProperty = Object.defineProperty; 9 | var result = $defineProperty(object, object, object) && $defineProperty; 10 | } catch(error) {} 11 | return result; 12 | }()); 13 | var stringFromCharCode = String.fromCharCode; 14 | var floor = Math.floor; 15 | var fromCodePoint = function(_) { 16 | var MAX_SIZE = 0x4000; 17 | var codeUnits = []; 18 | var highSurrogate; 19 | var lowSurrogate; 20 | var index = -1; 21 | var length = arguments.length; 22 | if (!length) { 23 | return ''; 24 | } 25 | var result = ''; 26 | while (++index < length) { 27 | var codePoint = Number(arguments[index]); 28 | if ( 29 | !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` 30 | codePoint < 0 || // not a valid Unicode code point 31 | codePoint > 0x10FFFF || // not a valid Unicode code point 32 | floor(codePoint) != codePoint // not an integer 33 | ) { 34 | throw RangeError('Invalid code point: ' + codePoint); 35 | } 36 | if (codePoint <= 0xFFFF) { // BMP code point 37 | codeUnits.push(codePoint); 38 | } else { // Astral code point; split in surrogate halves 39 | // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae 40 | codePoint -= 0x10000; 41 | highSurrogate = (codePoint >> 10) + 0xD800; 42 | lowSurrogate = (codePoint % 0x400) + 0xDC00; 43 | codeUnits.push(highSurrogate, lowSurrogate); 44 | } 45 | if (index + 1 == length || codeUnits.length > MAX_SIZE) { 46 | result += stringFromCharCode.apply(null, codeUnits); 47 | codeUnits.length = 0; 48 | } 49 | } 50 | return result; 51 | }; 52 | if (defineProperty) { 53 | defineProperty(String, 'fromCodePoint', { 54 | 'value': fromCodePoint, 55 | 'configurable': true, 56 | 'writable': true 57 | }); 58 | } else { 59 | String.fromCodePoint = fromCodePoint; 60 | } 61 | }()); 62 | } 63 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/tree/Tree.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | /// 6 | 7 | // The basic notion of a tree has a parent, a payload, and a list of children. 8 | // It is the most abstract interface for all the trees used by ANTLR. 9 | /// 10 | 11 | var Token = require('./../Token').Token; 12 | var Interval = require('./../IntervalSet').Interval; 13 | var INVALID_INTERVAL = new Interval(-1, -2); 14 | var Utils = require('../Utils.js'); 15 | 16 | 17 | function Tree() { 18 | return this; 19 | } 20 | 21 | function SyntaxTree() { 22 | Tree.call(this); 23 | return this; 24 | } 25 | 26 | SyntaxTree.prototype = Object.create(Tree.prototype); 27 | SyntaxTree.prototype.constructor = SyntaxTree; 28 | 29 | function ParseTree() { 30 | SyntaxTree.call(this); 31 | return this; 32 | } 33 | 34 | ParseTree.prototype = Object.create(SyntaxTree.prototype); 35 | ParseTree.prototype.constructor = ParseTree; 36 | 37 | function RuleNode() { 38 | ParseTree.call(this); 39 | return this; 40 | } 41 | 42 | RuleNode.prototype = Object.create(ParseTree.prototype); 43 | RuleNode.prototype.constructor = RuleNode; 44 | 45 | function TerminalNode() { 46 | ParseTree.call(this); 47 | return this; 48 | } 49 | 50 | TerminalNode.prototype = Object.create(ParseTree.prototype); 51 | TerminalNode.prototype.constructor = TerminalNode; 52 | 53 | function ErrorNode() { 54 | TerminalNode.call(this); 55 | return this; 56 | } 57 | 58 | ErrorNode.prototype = Object.create(TerminalNode.prototype); 59 | ErrorNode.prototype.constructor = ErrorNode; 60 | 61 | function ParseTreeVisitor() { 62 | return this; 63 | } 64 | 65 | ParseTreeVisitor.prototype.visit = function(ctx) { 66 | if (Array.isArray(ctx)) { 67 | return ctx.map(function(child) { 68 | return child.accept(this); 69 | }, this); 70 | } else { 71 | return ctx.accept(this); 72 | } 73 | }; 74 | 75 | ParseTreeVisitor.prototype.visitChildren = function(ctx) { 76 | return this.visit(ctx.children); 77 | } 78 | 79 | ParseTreeVisitor.prototype.visitTerminal = function(node) { 80 | }; 81 | 82 | ParseTreeVisitor.prototype.visitErrorNode = function(node) { 83 | }; 84 | 85 | 86 | function ParseTreeListener() { 87 | return this; 88 | } 89 | 90 | ParseTreeListener.prototype.visitTerminal = function(node) { 91 | }; 92 | 93 | ParseTreeListener.prototype.visitErrorNode = function(node) { 94 | }; 95 | 96 | ParseTreeListener.prototype.enterEveryRule = function(node) { 97 | }; 98 | 99 | ParseTreeListener.prototype.exitEveryRule = function(node) { 100 | }; 101 | 102 | function TerminalNodeImpl(symbol) { 103 | TerminalNode.call(this); 104 | this.parentCtx = null; 105 | this.symbol = symbol; 106 | return this; 107 | } 108 | 109 | TerminalNodeImpl.prototype = Object.create(TerminalNode.prototype); 110 | TerminalNodeImpl.prototype.constructor = TerminalNodeImpl; 111 | 112 | TerminalNodeImpl.prototype.getChild = function(i) { 113 | return null; 114 | }; 115 | 116 | TerminalNodeImpl.prototype.getSymbol = function() { 117 | return this.symbol; 118 | }; 119 | 120 | TerminalNodeImpl.prototype.getParent = function() { 121 | return this.parentCtx; 122 | }; 123 | 124 | TerminalNodeImpl.prototype.getPayload = function() { 125 | return this.symbol; 126 | }; 127 | 128 | TerminalNodeImpl.prototype.getSourceInterval = function() { 129 | if (this.symbol === null) { 130 | return INVALID_INTERVAL; 131 | } 132 | var tokenIndex = this.symbol.tokenIndex; 133 | return new Interval(tokenIndex, tokenIndex); 134 | }; 135 | 136 | TerminalNodeImpl.prototype.getChildCount = function() { 137 | return 0; 138 | }; 139 | 140 | TerminalNodeImpl.prototype.accept = function(visitor) { 141 | return visitor.visitTerminal(this); 142 | }; 143 | 144 | TerminalNodeImpl.prototype.getText = function() { 145 | return this.symbol.text; 146 | }; 147 | 148 | TerminalNodeImpl.prototype.toString = function() { 149 | if (this.symbol.type === Token.EOF) { 150 | return ""; 151 | } else { 152 | return this.symbol.text; 153 | } 154 | }; 155 | 156 | // Represents a token that was consumed during resynchronization 157 | // rather than during a valid match operation. For example, 158 | // we will create this kind of a node during single token insertion 159 | // and deletion as well as during "consume until error recovery set" 160 | // upon no viable alternative exceptions. 161 | 162 | function ErrorNodeImpl(token) { 163 | TerminalNodeImpl.call(this, token); 164 | return this; 165 | } 166 | 167 | ErrorNodeImpl.prototype = Object.create(TerminalNodeImpl.prototype); 168 | ErrorNodeImpl.prototype.constructor = ErrorNodeImpl; 169 | 170 | ErrorNodeImpl.prototype.isErrorNode = function() { 171 | return true; 172 | }; 173 | 174 | ErrorNodeImpl.prototype.accept = function(visitor) { 175 | return visitor.visitErrorNode(this); 176 | }; 177 | 178 | function ParseTreeWalker() { 179 | return this; 180 | } 181 | 182 | ParseTreeWalker.prototype.walk = function(listener, t) { 183 | var errorNode = t instanceof ErrorNode || 184 | (t.isErrorNode !== undefined && t.isErrorNode()); 185 | if (errorNode) { 186 | listener.visitErrorNode(t); 187 | } else if (t instanceof TerminalNode) { 188 | listener.visitTerminal(t); 189 | } else { 190 | this.enterRule(listener, t); 191 | for (var i = 0; i < t.getChildCount(); i++) { 192 | var child = t.getChild(i); 193 | this.walk(listener, child); 194 | } 195 | this.exitRule(listener, t); 196 | } 197 | }; 198 | // 199 | // The discovery of a rule node, involves sending two events: the generic 200 | // {@link ParseTreeListener//enterEveryRule} and a 201 | // {@link RuleContext}-specific event. First we trigger the generic and then 202 | // the rule specific. We to them in reverse order upon finishing the node. 203 | // 204 | ParseTreeWalker.prototype.enterRule = function(listener, r) { 205 | var ctx = r.getRuleContext(); 206 | listener.enterEveryRule(ctx); 207 | ctx.enterRule(listener); 208 | }; 209 | 210 | ParseTreeWalker.prototype.exitRule = function(listener, r) { 211 | var ctx = r.getRuleContext(); 212 | ctx.exitRule(listener); 213 | listener.exitEveryRule(ctx); 214 | }; 215 | 216 | ParseTreeWalker.DEFAULT = new ParseTreeWalker(); 217 | 218 | exports.RuleNode = RuleNode; 219 | exports.ErrorNode = ErrorNode; 220 | exports.TerminalNode = TerminalNode; 221 | exports.ErrorNodeImpl = ErrorNodeImpl; 222 | exports.TerminalNodeImpl = TerminalNodeImpl; 223 | exports.ParseTreeListener = ParseTreeListener; 224 | exports.ParseTreeVisitor = ParseTreeVisitor; 225 | exports.ParseTreeWalker = ParseTreeWalker; 226 | exports.INVALID_INTERVAL = INVALID_INTERVAL; 227 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/antlr4/tree/Trees.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | var Utils = require('./../Utils'); 7 | var Token = require('./../Token').Token; 8 | var RuleNode = require('./Tree').RuleNode; 9 | var ErrorNode = require('./Tree').ErrorNode; 10 | var TerminalNode = require('./Tree').TerminalNode; 11 | var ParserRuleContext = require('./../ParserRuleContext').ParserRuleContext; 12 | var RuleContext = require('./../RuleContext').RuleContext; 13 | var INVALID_ALT_NUMBER = require('./../atn/ATN').INVALID_ALT_NUMBER; 14 | 15 | 16 | /** A set of utility routines useful for all kinds of ANTLR trees. */ 17 | function Trees() { 18 | } 19 | 20 | // Print out a whole tree in LISP form. {@link //getNodeText} is used on the 21 | // node payloads to get the text for the nodes. Detect 22 | // parse trees and extract data appropriately. 23 | Trees.toStringTree = function(tree, ruleNames, recog) { 24 | ruleNames = ruleNames || null; 25 | recog = recog || null; 26 | if(recog!==null) { 27 | ruleNames = recog.ruleNames; 28 | } 29 | var s = Trees.getNodeText(tree, ruleNames); 30 | s = Utils.escapeWhitespace(s, false); 31 | var c = tree.getChildCount(); 32 | if(c===0) { 33 | return s; 34 | } 35 | var res = "(" + s + ' '; 36 | if(c>0) { 37 | s = Trees.toStringTree(tree.getChild(0), ruleNames); 38 | res = res.concat(s); 39 | } 40 | for(var i=1;i visit(child, visitor)) 86 | } 87 | 88 | if (!_isASTNode(node)) return 89 | 90 | let cont = true 91 | 92 | if (visitor[node.type]) { 93 | cont = visitor[node.type](node) 94 | } 95 | 96 | if (cont === false) return 97 | 98 | for (const prop in node) { 99 | if (node.hasOwnProperty(prop)) { 100 | visit(node[prop], visitor) 101 | } 102 | } 103 | 104 | const selector = node.type + ':exit' 105 | if (visitor[selector]) { 106 | visitor[selector](node) 107 | } 108 | } 109 | 110 | exports.tokenize = tokenize 111 | exports.parse = parse 112 | exports.visit = visit 113 | exports.ParserError = ParserError 114 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/lib/Solidity.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | T__14=15 16 | T__15=16 17 | T__16=17 18 | T__17=18 19 | T__18=19 20 | T__19=20 21 | T__20=21 22 | T__21=22 23 | T__22=23 24 | T__23=24 25 | T__24=25 26 | T__25=26 27 | T__26=27 28 | T__27=28 29 | T__28=29 30 | T__29=30 31 | T__30=31 32 | T__31=32 33 | T__32=33 34 | T__33=34 35 | T__34=35 36 | T__35=36 37 | T__36=37 38 | T__37=38 39 | T__38=39 40 | T__39=40 41 | T__40=41 42 | T__41=42 43 | T__42=43 44 | T__43=44 45 | T__44=45 46 | T__45=46 47 | T__46=47 48 | T__47=48 49 | T__48=49 50 | T__49=50 51 | T__50=51 52 | T__51=52 53 | T__52=53 54 | T__53=54 55 | T__54=55 56 | T__55=56 57 | T__56=57 58 | T__57=58 59 | T__58=59 60 | T__59=60 61 | T__60=61 62 | T__61=62 63 | T__62=63 64 | T__63=64 65 | T__64=65 66 | T__65=66 67 | T__66=67 68 | T__67=68 69 | T__68=69 70 | T__69=70 71 | T__70=71 72 | T__71=72 73 | T__72=73 74 | T__73=74 75 | T__74=75 76 | T__75=76 77 | T__76=77 78 | T__77=78 79 | T__78=79 80 | T__79=80 81 | T__80=81 82 | T__81=82 83 | T__82=83 84 | T__83=84 85 | T__84=85 86 | T__85=86 87 | T__86=87 88 | T__87=88 89 | T__88=89 90 | T__89=90 91 | Int=91 92 | Uint=92 93 | Byte=93 94 | Fixed=94 95 | Ufixed=95 96 | VersionLiteral=96 97 | BooleanLiteral=97 98 | DecimalNumber=98 99 | HexNumber=99 100 | NumberUnit=100 101 | HexLiteral=101 102 | ReservedKeyword=102 103 | AnonymousKeyword=103 104 | BreakKeyword=104 105 | ConstantKeyword=105 106 | ContinueKeyword=106 107 | ExternalKeyword=107 108 | IndexedKeyword=108 109 | InternalKeyword=109 110 | PayableKeyword=110 111 | PrivateKeyword=111 112 | PublicKeyword=112 113 | PureKeyword=113 114 | ViewKeyword=114 115 | Identifier=115 116 | StringLiteral=116 117 | WS=117 118 | COMMENT=118 119 | LINE_COMMENT=119 120 | 'pragma'=1 121 | ';'=2 122 | '^'=3 123 | '~'=4 124 | '>='=5 125 | '>'=6 126 | '<'=7 127 | '<='=8 128 | '='=9 129 | 'as'=10 130 | 'import'=11 131 | '*'=12 132 | 'from'=13 133 | '{'=14 134 | ','=15 135 | '}'=16 136 | 'contract'=17 137 | 'interface'=18 138 | 'library'=19 139 | 'is'=20 140 | '('=21 141 | ')'=22 142 | 'using'=23 143 | 'for'=24 144 | 'struct'=25 145 | 'constructor'=26 146 | 'modifier'=27 147 | 'function'=28 148 | 'returns'=29 149 | 'event'=30 150 | 'enum'=31 151 | '['=32 152 | ']'=33 153 | '.'=34 154 | 'mapping'=35 155 | '=>'=36 156 | 'memory'=37 157 | 'storage'=38 158 | 'calldata'=39 159 | 'if'=40 160 | 'else'=41 161 | 'while'=42 162 | 'assembly'=43 163 | 'do'=44 164 | 'return'=45 165 | 'throw'=46 166 | 'emit'=47 167 | 'var'=48 168 | 'address'=49 169 | 'bool'=50 170 | 'string'=51 171 | 'byte'=52 172 | '++'=53 173 | '--'=54 174 | 'new'=55 175 | '+'=56 176 | '-'=57 177 | 'after'=58 178 | 'delete'=59 179 | '!'=60 180 | '**'=61 181 | '/'=62 182 | '%'=63 183 | '<<'=64 184 | '>>'=65 185 | '&'=66 186 | '|'=67 187 | '=='=68 188 | '!='=69 189 | '&&'=70 190 | '||'=71 191 | '?'=72 192 | ':'=73 193 | '|='=74 194 | '^='=75 195 | '&='=76 196 | '<<='=77 197 | '>>='=78 198 | '+='=79 199 | '-='=80 200 | '*='=81 201 | '/='=82 202 | '%='=83 203 | 'let'=84 204 | ':='=85 205 | '=:'=86 206 | 'switch'=87 207 | 'case'=88 208 | 'default'=89 209 | '->'=90 210 | 'anonymous'=103 211 | 'break'=104 212 | 'constant'=105 213 | 'continue'=106 214 | 'external'=107 215 | 'indexed'=108 216 | 'internal'=109 217 | 'payable'=110 218 | 'private'=111 219 | 'public'=112 220 | 'pure'=113 221 | 'view'=114 222 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/lib/SolidityLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | T__14=15 16 | T__15=16 17 | T__16=17 18 | T__17=18 19 | T__18=19 20 | T__19=20 21 | T__20=21 22 | T__21=22 23 | T__22=23 24 | T__23=24 25 | T__24=25 26 | T__25=26 27 | T__26=27 28 | T__27=28 29 | T__28=29 30 | T__29=30 31 | T__30=31 32 | T__31=32 33 | T__32=33 34 | T__33=34 35 | T__34=35 36 | T__35=36 37 | T__36=37 38 | T__37=38 39 | T__38=39 40 | T__39=40 41 | T__40=41 42 | T__41=42 43 | T__42=43 44 | T__43=44 45 | T__44=45 46 | T__45=46 47 | T__46=47 48 | T__47=48 49 | T__48=49 50 | T__49=50 51 | T__50=51 52 | T__51=52 53 | T__52=53 54 | T__53=54 55 | T__54=55 56 | T__55=56 57 | T__56=57 58 | T__57=58 59 | T__58=59 60 | T__59=60 61 | T__60=61 62 | T__61=62 63 | T__62=63 64 | T__63=64 65 | T__64=65 66 | T__65=66 67 | T__66=67 68 | T__67=68 69 | T__68=69 70 | T__69=70 71 | T__70=71 72 | T__71=72 73 | T__72=73 74 | T__73=74 75 | T__74=75 76 | T__75=76 77 | T__76=77 78 | T__77=78 79 | T__78=79 80 | T__79=80 81 | T__80=81 82 | T__81=82 83 | T__82=83 84 | T__83=84 85 | T__84=85 86 | T__85=86 87 | T__86=87 88 | T__87=88 89 | T__88=89 90 | T__89=90 91 | Int=91 92 | Uint=92 93 | Byte=93 94 | Fixed=94 95 | Ufixed=95 96 | VersionLiteral=96 97 | BooleanLiteral=97 98 | DecimalNumber=98 99 | HexNumber=99 100 | NumberUnit=100 101 | HexLiteral=101 102 | ReservedKeyword=102 103 | AnonymousKeyword=103 104 | BreakKeyword=104 105 | ConstantKeyword=105 106 | ContinueKeyword=106 107 | ExternalKeyword=107 108 | IndexedKeyword=108 109 | InternalKeyword=109 110 | PayableKeyword=110 111 | PrivateKeyword=111 112 | PublicKeyword=112 113 | PureKeyword=113 114 | ViewKeyword=114 115 | Identifier=115 116 | StringLiteral=116 117 | WS=117 118 | COMMENT=118 119 | LINE_COMMENT=119 120 | 'pragma'=1 121 | ';'=2 122 | '^'=3 123 | '~'=4 124 | '>='=5 125 | '>'=6 126 | '<'=7 127 | '<='=8 128 | '='=9 129 | 'as'=10 130 | 'import'=11 131 | '*'=12 132 | 'from'=13 133 | '{'=14 134 | ','=15 135 | '}'=16 136 | 'contract'=17 137 | 'interface'=18 138 | 'library'=19 139 | 'is'=20 140 | '('=21 141 | ')'=22 142 | 'using'=23 143 | 'for'=24 144 | 'struct'=25 145 | 'constructor'=26 146 | 'modifier'=27 147 | 'function'=28 148 | 'returns'=29 149 | 'event'=30 150 | 'enum'=31 151 | '['=32 152 | ']'=33 153 | '.'=34 154 | 'mapping'=35 155 | '=>'=36 156 | 'memory'=37 157 | 'storage'=38 158 | 'calldata'=39 159 | 'if'=40 160 | 'else'=41 161 | 'while'=42 162 | 'assembly'=43 163 | 'do'=44 164 | 'return'=45 165 | 'throw'=46 166 | 'emit'=47 167 | 'var'=48 168 | 'address'=49 169 | 'bool'=50 170 | 'string'=51 171 | 'byte'=52 172 | '++'=53 173 | '--'=54 174 | 'new'=55 175 | '+'=56 176 | '-'=57 177 | 'after'=58 178 | 'delete'=59 179 | '!'=60 180 | '**'=61 181 | '/'=62 182 | '%'=63 183 | '<<'=64 184 | '>>'=65 185 | '&'=66 186 | '|'=67 187 | '=='=68 188 | '!='=69 189 | '&&'=70 190 | '||'=71 191 | '?'=72 192 | ':'=73 193 | '|='=74 194 | '^='=75 195 | '&='=76 196 | '<<='=77 197 | '>>='=78 198 | '+='=79 199 | '-='=80 200 | '*='=81 201 | '/='=82 202 | '%='=83 203 | 'let'=84 204 | ':='=85 205 | '=:'=86 206 | 'switch'=87 207 | 'case'=88 208 | 'default'=89 209 | '->'=90 210 | 'anonymous'=103 211 | 'break'=104 212 | 'constant'=105 213 | 'continue'=106 214 | 'external'=107 215 | 'indexed'=108 216 | 'internal'=109 217 | 'payable'=110 218 | 'private'=111 219 | 'public'=112 220 | 'pure'=113 221 | 'view'=114 222 | -------------------------------------------------------------------------------- /packages/sooho-parser/src/tokens.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const TYPE_TOKENS = [ 5 | 'var', 6 | 'bool', 7 | 'address', 8 | 'string', 9 | 'Int', 10 | 'Uint', 11 | 'Byte', 12 | 'Fixed', 13 | 'UFixed' 14 | ] 15 | 16 | function rsplit(str, value) { 17 | const index = str.lastIndexOf(value) 18 | return [str.substring(0, index), str.substring(index + 1, str.length)] 19 | } 20 | 21 | function normalizeTokenType(value) { 22 | if (value.endsWith("'")) { 23 | value = value.substring(0, value.length - 1) 24 | } 25 | if (value.startsWith("'")) { 26 | value = value.substring(1, value.length) 27 | } 28 | return value 29 | } 30 | 31 | function getTokenType(value) { 32 | if (value === 'Identifier' || value === 'from') { 33 | return 'Identifier' 34 | } else if (value === 'TrueLiteral' || value === 'FalseLiteral') { 35 | return 'Boolean' 36 | } else if (value === 'VersionLiteral') { 37 | return 'Version' 38 | } else if (value === 'StringLiteral') { 39 | return 'String' 40 | } else if (TYPE_TOKENS.includes(value)) { 41 | return 'Type' 42 | } else if (value === 'NumberUnit') { 43 | return 'Subdenomination' 44 | } else if (value === 'DecimalNumber') { 45 | return 'Numeric' 46 | } else if (value === 'HexLiteral') { 47 | return 'Hex' 48 | } else if (value === 'ReservedKeyword') { 49 | return 'Reserved' 50 | } else if (/^\W+$/.test(value)) { 51 | return 'Punctuator' 52 | } else { 53 | return 'Keyword' 54 | } 55 | } 56 | 57 | function getTokenTypeMap() { 58 | const filePath = path.join(__dirname, './lib/Solidity.tokens') 59 | 60 | return fs 61 | .readFileSync(filePath) 62 | .toString('utf-8') 63 | .split('\n') 64 | .map(line => rsplit(line, '=')) 65 | .reduce((acum, [value, key]) => { 66 | acum[parseInt(key, 10)] = normalizeTokenType(value) 67 | return acum 68 | }, {}) 69 | } 70 | 71 | function buildTokenList(tokens, options) { 72 | const tokenTypes = getTokenTypeMap() 73 | 74 | return tokens.map(token => { 75 | const type = getTokenType(tokenTypes[token.type]) 76 | const node = { type, value: token.text } 77 | if (options.range) { 78 | node.range = [token.start, token.stop + 1] 79 | } 80 | if (options.loc) { 81 | node.loc = { 82 | start: { line: token.line, column: token.column }, 83 | end: { line: token.line, column: token.column + token.text.length } 84 | } 85 | } 86 | return node 87 | }) 88 | } 89 | 90 | exports.buildTokenList = buildTokenList 91 | -------------------------------------------------------------------------------- /packages/sooho-parser/test/index.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs") 2 | var { assert } = require('chai') 3 | var parser = require("../src/index") 4 | var { parseNode, parseStatement } = require('./utils') 5 | 6 | describe("#parse", () => { 7 | 8 | it("parses test file correctly", () => { 9 | var content = fs.readFileSync(__dirname + "/test.sol") 10 | parser.parse(content.toString()) 11 | }) 12 | 13 | it("throws ParserError on syntax error", () => { 14 | var source = "not good" 15 | assert.throws(() => { 16 | parser.parse(source) 17 | }, parser.ParseError) 18 | }) 19 | 20 | it("supports tolerant mode", () => { 21 | var source = "not good" 22 | var root = parser.parse(source, { tolerant: true }) 23 | assert.equal(root.errors.length, 1) 24 | }) 25 | 26 | it("supports loc", () => { 27 | var source = "contract test { uint a; }" 28 | var root = parser.parse(source, { loc: true }) 29 | assert.isOk(root.hasOwnProperty('loc')) 30 | }) 31 | 32 | it("supports range", () => { 33 | var source = "contract test { uint a; }" 34 | var root = parser.parse(source, { range: true }) 35 | assert.isOk(root.hasOwnProperty('range')) 36 | }) 37 | 38 | it('can build ast with tolerant mode errors', () => { 39 | // TODO: just a few examples here, more should be added 40 | var cases = [ 41 | 'contract { function a() return bool {} }', 42 | 'contract test { function () { 2 + + 2; } }', 43 | 'contract test { uint ; }', 44 | 'contract test { modifier { } }' 45 | ] 46 | 47 | for (var c of cases) { 48 | parser.parse(c, { tolerant: true }) 49 | } 50 | }) 51 | 52 | 53 | describe("node meta", () => { 54 | 55 | it("adds meta to VariableDeclaration inside StateVariableDeclaration", () => { 56 | var ast = parseNode("uint public a;", { loc: true }) 57 | assert.isOk(ast.variables[0].loc) 58 | }) 59 | 60 | it("adds meta to VariableDeclaration inside VariableDeclarationStatement", () => { 61 | var ast = parseStatement("uint a;", { loc: true }) 62 | assert.isOk(ast.variables[0].loc) 63 | }) 64 | 65 | it("adds meta to VariableDeclaration inside EventDefinition", () => { 66 | var ast = parseNode("event Foo(address bar);", { loc: true }) 67 | assert.isOk(ast.parameters.parameters[0].loc) 68 | }) 69 | 70 | }) 71 | }) 72 | 73 | describe("#visit", () => { 74 | 75 | it("walks visitor through AST", () => { 76 | var source = "contract test { uint a; }" 77 | var ast = parser.parse(source) 78 | parser.visit(ast, { 79 | ContractDefinition: (node) => { 80 | assert.equal(node.type, 'ContractDefinition') 81 | }, 82 | 83 | 'ContractDefinition:exit': (node) => { 84 | assert.equal(node.type, 'ContractDefinition') 85 | } 86 | }) 87 | }) 88 | 89 | it("can stop visiting inner nodes by returning false", () => { 90 | var source = "contract test { uint a; }" 91 | var ast = parser.parse(source) 92 | parser.visit(ast, { 93 | ContractDefinition: (node) => { 94 | return false 95 | }, 96 | 97 | 'ContractDefinition:exit': (node) => { 98 | assert.fail('should not reach here') 99 | } 100 | }) 101 | }) 102 | 103 | it("shouldn't print anything if the lexer fails", () => { 104 | const originalConsoleError = console.error 105 | let called = false 106 | console.error = () => called = true 107 | 108 | var ast = parser.parse('"', {tolerant: true}) 109 | 110 | console.error = originalConsoleError 111 | 112 | assert.isFalse(called, "Should not call console.error on lexer errors") 113 | }); 114 | 115 | }) 116 | -------------------------------------------------------------------------------- /packages/sooho-parser/test/utils.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const parser = require('../src/index') 3 | 4 | function print(obj) { 5 | console.log(JSON.stringify(obj, null, 2)) 6 | } 7 | 8 | function parseContract(source, options = {}) { 9 | var ast = parser.parse(source, options) 10 | assert.isOk(ast.children[0]) 11 | return ast.children[0] 12 | } 13 | 14 | function parseNode(source, options = {}) { 15 | var contract = parseContract("contract test { " + source + " }", options) 16 | assert.isOk(contract.subNodes[0]) 17 | return contract.subNodes[0] 18 | } 19 | 20 | function parseStatement(source, options = {}) { 21 | var ast = parseNode("function () { " + source + " }", options) 22 | assert.isOk(ast.body.statements[0]) 23 | return ast.body.statements[0] 24 | } 25 | 26 | function parseExpression(source, options = {}) { 27 | var ast = parseNode("function () { " + source + "; }", options) 28 | assert.isOk(ast.body.statements[0].expression) 29 | return ast.body.statements[0].expression 30 | } 31 | 32 | function parseAssembly(source, options = {}) { 33 | var ast = parseNode("function () { assembly { " + source + " } }", options) 34 | assert.isOk(ast.body.statements[0].body.operations[0]) 35 | return ast.body.statements[0].body.operations[0] 36 | } 37 | 38 | module.exports = { 39 | parseAssembly, 40 | parseContract, 41 | parseExpression, 42 | parseNode, 43 | parseStatement, 44 | print 45 | } 46 | --------------------------------------------------------------------------------