├── public ├── favicon.ico ├── fonts │ ├── icomoon.eot │ ├── icomoon.ttf │ ├── icomoon.woff │ └── icomoon.svg ├── manifest.json └── index.html ├── .gitattributes ├── src ├── Components │ ├── Tooltip.js │ ├── FeatureProperty.js │ ├── ToggledOption.js │ ├── BusySignal.js │ ├── RadioButtonGroup.js │ ├── OutputButtonGroup.js │ ├── AboutButton.js │ ├── CompilerButton.js │ ├── SettingsButton.js │ ├── CompileButton.js │ └── Editor.js ├── logo.svg ├── index.js ├── Pages │ └── NotFound.js ├── Common │ ├── Base64.js │ └── Common.js ├── App.js ├── registerServiceWorker.js ├── monaco.css ├── Grammars │ ├── theme.js │ └── wast.js ├── index.css ├── toggle.button.css └── Containers │ ├── ToolbarContainer.js │ ├── FooterContainer.js │ └── EditorContainer.js ├── webpack.config.dev.extension.js ├── webpack.config.prod.extension.js ├── .gitignore ├── CHANGELOG.md ├── README.md ├── package.json ├── scripts └── custom-config.js └── LICENSE /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxGraey/Assembleash/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxGraey/Assembleash/HEAD/public/fonts/icomoon.eot -------------------------------------------------------------------------------- /public/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxGraey/Assembleash/HEAD/public/fonts/icomoon.ttf -------------------------------------------------------------------------------- /public/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxGraey/Assembleash/HEAD/public/fonts/icomoon.woff -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text 3 | *.css text 4 | 5 | # help to Github linguist determine this code as JavaScript mostly 6 | *.css linguist-language=JavaScript 7 | -------------------------------------------------------------------------------- /src/Components/Tooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from 'react-bootstrap/lib/Tooltip'; 3 | import moize from 'moize'; 4 | 5 | export default moize.react( 6 | (desc = '') =>

{ desc }

7 | ); 8 | -------------------------------------------------------------------------------- /webpack.config.dev.extension.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CopyPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = function (config) { 5 | config.plugins.unshift(new CopyPlugin([{ 6 | from: 'node_modules/monaco-editor/min/vs', 7 | to: 'vs' 8 | }])); 9 | 10 | return config; 11 | }; 12 | -------------------------------------------------------------------------------- /webpack.config.prod.extension.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CopyPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = function (config) { 5 | config.plugins.unshift(new CopyPlugin([{ 6 | from: 'node_modules/monaco-editor/min/vs', 7 | to: 'vs' 8 | }])); 9 | 10 | return config; 11 | }; 12 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | /save 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './App'; 5 | 6 | import registerServiceWorker from './registerServiceWorker'; 7 | 8 | import './index.css' 9 | import './bootstrap.theme.css' 10 | import './toggle.button.css' 11 | import './monaco.css' 12 | 13 | ReactDOM.render(, document.getElementById('root')); 14 | registerServiceWorker(); 15 | -------------------------------------------------------------------------------- /src/Pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import {Link} from 'react-router'; 3 | 4 | export default class NotFoundPage extends Component { 5 | render() { 6 | return ( 7 |
8 |

404

9 |

Page not found!

10 |

11 | {/* Go back to the main page */} 12 |

13 |
14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Components/FeatureProperty.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | // import Badge from 'react-bootstrap/lib/Badge'; 4 | // import Button from 'react-bootstrap/lib/Button'; 5 | // import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 6 | // import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 7 | 8 | export default class FeatureProperty extends PureComponent { 9 | render() { 10 | return ( 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### 1.0.0-beta.7 2 | * Continue integration AssemblyScript NEXT 3 | * Minor improvements 4 | 5 | --- 6 | 7 | #### 1.0.0-beta.6 8 | * Replace AssemblyScript by AssemblyScript NEXT (new architecture) 9 | * Reduce bundle by over 100kb 10 | * A lot of optimizations & code refactoring 11 | 12 | --- 13 | 14 | #### 1.0.0-beta.5 15 | * Remove TurboScript from supporting compilers 16 | 17 | --- 18 | 19 | #### 1.0.0-beta.4 20 | * Update monaco-editor 21 | * Migrate to React v16.x 22 | 23 | --- 24 | 25 | #### 1.0.0-beta.2 26 | * Add TurboScript compiler 27 | 28 | --- 29 | 30 | #### 1.0.0-beta.1 31 | * Add Speedy.js copmiler 32 | 33 | --- 34 | 35 | #### 1.0.0-beta 36 | * Init 37 | -------------------------------------------------------------------------------- /src/Common/Base64.js: -------------------------------------------------------------------------------- 1 | 2 | const encodeBatchSize = 0x8000; 3 | 4 | export default class Base64 { 5 | static encode(array) { 6 | let result = ''; 7 | 8 | if (!array || array[0] === void 0) 9 | return result; 10 | 11 | if (Array.isArray(array) && !array.subarray) 12 | array = new Uint8Array(array); 13 | 14 | for (var i = 0, len = array.length; i < len; i += encodeBatchSize) 15 | result += btoa(String.fromCharCode.apply(null, array.subarray(i, i + encodeBatchSize))); 16 | 17 | return result; 18 | } 19 | 20 | static decode(input) { 21 | if (typeof input !== 'string') return new Uint8Array(); 22 | return new Uint8Array(atob(input).split('').map(char => { 23 | return char.charCodeAt(0); 24 | })); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Deprecated. 2 | --- 3 | If you need online playground for AssemblyScript you should use [WebAssembly Studio](https://webassembly.studio) 4 | 5 | ~~Try [Assembleash Playground](https://maxgraey.github.io/Assembleash/#AssemblyScript)~~ 6 | 7 | [![](https://tokei.rs/b1/github/MaxGraey/Assembleash?style=flat-square)](https://github.com/MaxGraey/Assembleash) 8 | 9 | Online playground for WebAssembly and Typescript-like languages. 10 | 11 | **Support languages:** 12 | 13 | - AssemblyScript NEXT 14 | - Speedy.js 15 | 16 | --- 17 | 18 | 19 | ![screenshot](https://www.dropbox.com/s/36mexi7cjc7zvhg/assembleash-screenshot.jpg?raw=1) 20 | 21 | #### TODO 22 | - [ ] Migrate to reactstrap 23 | 24 | ### Contribute 25 | PRs accepted. 26 | 27 | ### License 28 | [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0) © MaxGraey 29 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import EditorContainer from './Containers/EditorContainer' 4 | import { CompilerList } from './Common/Common' 5 | 6 | export default class App extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | compiler: CompilerList[0] // AssemblyScript 11 | } 12 | } 13 | 14 | componentWillMount() { 15 | // remove spinner 16 | let spinner = document.getElementById('load-spinner'); 17 | document.body.removeChild(spinner); 18 | 19 | let compiler = window.location.hash.substring(1); 20 | if (compiler) { 21 | CompilerList.forEach(value => { 22 | if (compiler.toLowerCase() === value.toLowerCase()) { 23 | this.setState({ compiler }); 24 | } 25 | }); 26 | } 27 | } 28 | 29 | render() { 30 | const { compiler } = this.state; 31 | return ( 32 | 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assembleash-playground", 3 | "version": "1.0.0-beta.7", 4 | "description": "WebAssembly and Typescript-like languages playground", 5 | "homepage": "http://MaxGraey.github.io/Assembleash", 6 | "author": "Max Graey ", 7 | "license": "Apache-2", 8 | "keywords": [ 9 | "webassembly", 10 | "wasm", 11 | "playground", 12 | "assemblyscript", 13 | "typescript", 14 | "compiler", 15 | "online" 16 | ], 17 | "dependencies": { 18 | "classnames": "^2.2.5", 19 | "file-saver": "^1.3.8", 20 | "immutable": "^3.8.2", 21 | "moize": "^5.2.1", 22 | "react": "^16.3.1", 23 | "react-bootstrap": "^0.32.1", 24 | "react-dom": "^16.3.1", 25 | "react-monaco-editor": "^0.14.1", 26 | "react-notification": "^6.8.2", 27 | "react-split-pane": "^0.1.77", 28 | "react-switch-button": "^2.3.3", 29 | "react-toggle": "^4.0.2", 30 | "throttle-debounce": "^1.0.1" 31 | }, 32 | "devDependencies": { 33 | "copy-webpack-plugin": "^4.5.1", 34 | "proxyquire": "^2.0.1", 35 | "react-scripts": "^1.1.4", 36 | "react-svg-loader": "2.1.0", 37 | "rewire": "^4.0.0" 38 | }, 39 | "scripts": { 40 | "start": "node scripts/custom-config start", 41 | "build": "node scripts/custom-config build", 42 | "test": "react-scripts test --env=jsdom", 43 | "eject": "react-scripts eject", 44 | "predeploy": "yarn build", 45 | "deploy": "gh-pages -d build" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Components/ToggledOption.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Toggle from 'react-toggle'; 3 | 4 | export default class ToggledOption extends Component { 5 | static defualtProps = { 6 | active: false, 7 | defaultActive: false, 8 | onChange: () => {}, 9 | } 10 | 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | active: props.active || props.defaultActive 15 | }; 16 | } 17 | 18 | onChange = () => { 19 | this.setState(({ active }) => ({ active: !active })); 20 | // We can call onChange in parallel manner without wait of state changes 21 | this.props.onChange(!this.state.active); 22 | } 23 | 24 | render() { 25 | const { label } = this.props; 26 | return ( 27 |
36 |

37 | 41 | { label } 42 | 43 | 44 | 49 |

50 |
51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Components/BusySignal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 5 | 6 | export default class BusySignal extends Component { 7 | 8 | static propTypes = { 9 | state: PropTypes.oneOf(['pending', 'success', 'failure']) 10 | } 11 | 12 | static defaultProps = { 13 | state: 'pending' 14 | } 15 | 16 | _renderSpinner() { 17 | const state = this.props.state; 18 | return ( 19 | state === 'pending' ? 20 |
21 |
22 |
23 |
24 |
: null 25 | ); 26 | } 27 | 28 | _renderDot() { 29 | return ( 30 | this.props.state === 'pending' ? 31 |
32 | : null 33 | ); 34 | } 35 | 36 | _renderReadyState() { 37 | const state = this.props.state; 38 | const success = state === 'success'; 39 | if (success || state === 'failure') 40 | return ( 41 | 45 | ); 46 | 47 | return null; 48 | } 49 | 50 | render() { 51 | const className = this.props.className + " busy-signal"; 52 | return ( 53 |
54 | { this._renderSpinner() } 55 | { this._renderDot() } 56 | { this._renderReadyState() } 57 |
58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /public/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Components/RadioButtonGroup.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; 5 | 6 | export default class RadioButtonGroup extends PureComponent { 7 | 8 | static propTypes = { 9 | children: PropTypes.node, 10 | type: PropTypes.oneOf(['checkbox', 'radio']), 11 | value: PropTypes.object, 12 | onChange: PropTypes.func, 13 | valueLink: PropTypes.shape({ 14 | value: PropTypes.any, 15 | requestChange: PropTypes.func.isRequired, 16 | }), 17 | } 18 | 19 | static defaultProps = { 20 | value: {}, 21 | valueLink: null, 22 | onChange: () => {}, 23 | } 24 | 25 | binding(props) { 26 | const { onChange, value } = props; 27 | return props.valueLink || { 28 | requestChange: onChange, 29 | value, 30 | }; 31 | } 32 | 33 | onClick(child) { 34 | const { value, requestChange } = this.binding(this.props); 35 | 36 | let keys = React.Children.map(this.props.children, child => child.props.eventKey), 37 | key = child.props.eventKey; 38 | 39 | if (this.props.type === 'radio') { 40 | keys.forEach(k => { value[k] = k === key }); 41 | } else { 42 | keys.forEach(k => { 43 | value[k] = (k === key) ? !value[k] : value[k]; 44 | }); 45 | } 46 | requestChange(value); 47 | } 48 | 49 | renderButtons() { 50 | const value = this.binding(this.props).value || {}; 51 | return React.Children.map(this.props.children, child => { 52 | const active = value[child.props.eventKey] || false; 53 | return React.cloneElement(child, { 54 | onClick: () => this.onClick(child), 55 | active, 56 | }) 57 | }); 58 | } 59 | 60 | render() { 61 | const { className, bsSize } = this.props; 62 | return ( 63 | 64 | { this.renderButtons() } 65 | 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Components/OutputButtonGroup.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import Button from 'react-bootstrap/lib/Button'; 4 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 5 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 6 | 7 | import tooltip from './Tooltip'; 8 | import RadioButtonGroup from './RadioButtonGroup'; 9 | 10 | export default class OutputButtonGroup extends Component { 11 | static defaultProps = { 12 | onSelect: () => {}, 13 | } 14 | 15 | state = { 16 | radio: { 17 | text: true, 18 | binary: false, 19 | }, 20 | } 21 | 22 | onSelect = values => { 23 | this.setState({ radio: values }); 24 | this.props.onSelect(this._getOutputType()); 25 | } 26 | 27 | _getOutputType() { 28 | const { text, binary } = this.state.radio; 29 | if (text) return 'text'; 30 | if (binary) return 'binary'; 31 | return 'text'; 32 | } 33 | 34 | render() { 35 | return ( 36 | 42 | 49 | 52 | 55 | 56 | 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Components/AboutButton.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { PureComponent } from 'react'; 3 | 4 | import Badge from 'react-bootstrap/lib/Badge'; 5 | import Button from 'react-bootstrap/lib/Button'; 6 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 7 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 8 | import Popover from 'react-bootstrap/lib/Popover'; 9 | 10 | // import FeatureProperty from './FeatureProperty' 11 | import { CompilerDescriptions } from '../Common/Common' 12 | 13 | export default class AboutButton extends PureComponent { 14 | static defaultProps = { 15 | compiler: 'TurboScript', 16 | version: '0.0.0', 17 | } 18 | 19 | _renderOverlay() { 20 | const { compiler, version } = this.props; 21 | const title = ( 22 | 23 |
About
24 |

{ compiler + ' ' }

25 | { version } 26 |
27 | ); 28 | 29 | const description = CompilerDescriptions[compiler] || {}; 30 | const github = description.github || ''; 31 | 32 | return ( 33 | 38 | 43 | 44 | ); 45 | } 46 | 47 | render() { 48 | return ( 49 | 55 | 61 | 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | export default function register() { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | window.addEventListener('load', () => { 14 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 15 | navigator.serviceWorker 16 | .register(swUrl) 17 | .then(registration => { 18 | registration.onupdatefound = () => { 19 | const installingWorker = registration.installing; 20 | installingWorker.onstatechange = () => { 21 | if (installingWorker.state === 'installed') { 22 | if (navigator.serviceWorker.controller) { 23 | // At this point, the old content will have been purged and 24 | // the fresh content will have been added to the cache. 25 | // It's the perfect time to display a "New content is 26 | // available; please refresh." message in your web app. 27 | console.log('New content is available; please refresh.'); 28 | } else { 29 | // At this point, everything has been precached. 30 | // It's the perfect time to display a 31 | // "Content is cached for offline use." message. 32 | console.log('Content is cached for offline use.'); 33 | } 34 | } 35 | }; 36 | }; 37 | }) 38 | .catch(error => { 39 | console.error('Error during service worker registration:', error); 40 | }); 41 | }); 42 | } 43 | } 44 | 45 | export function unregister() { 46 | if ('serviceWorker' in navigator) { 47 | navigator.serviceWorker.ready.then(registration => { 48 | registration.unregister(); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Components/CompilerButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import DropdownButton from 'react-bootstrap/lib/DropdownButton'; 5 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 6 | import MenuItem from 'react-bootstrap/lib/MenuItem'; 7 | 8 | import tooltip from './Tooltip'; 9 | 10 | import { CompilerList } from '../Common/Common'; 11 | 12 | export default class CompilerButton extends Component { 13 | static defaultProps = { 14 | compiler: CompilerList[0], 15 | onSelect: () => {}, 16 | } 17 | 18 | static propTypes = { 19 | compiler: PropTypes.string, 20 | onSelect: PropTypes.func, 21 | } 22 | 23 | static styles = { 24 | button: { 25 | minWidth: '238px', 26 | }, 27 | 28 | menuItem: { 29 | minWidth: '238px', 30 | textAlign: 'center', 31 | } 32 | } 33 | 34 | constructor(props) { 35 | super(props); 36 | this.state = { 37 | compiler: props.compiler, 38 | }; 39 | } 40 | 41 | onSelect = eventKey => { 42 | const compiler = CompilerList[eventKey]; 43 | this.setState({ compiler }, () => { 44 | this.props.onSelect(compiler); 45 | }); 46 | } 47 | 48 | render() { 49 | const { styles } = this.constructor; 50 | return ( 51 | 57 | 65 | { 66 | CompilerList.map((value, index) => ( 67 | 73 |

{ value }

74 |
75 | )) 76 | } 77 |
78 |
79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/monaco.css: -------------------------------------------------------------------------------- 1 | .monaco-editor { 2 | background: transparent !important; 3 | } 4 | 5 | .monaco-editor-background { 6 | background: transparent !important; 7 | } 8 | 9 | .margin-view-overlays { 10 | background-color: #252e37; 11 | } 12 | 13 | .margin-view-overlays .line-numbers { 14 | color: rgba(230, 210, 255, 0.25) !important; 15 | } 16 | 17 | .monaco-scrollable-element { 18 | background: rgba(0,0,0,0.25) !important; 19 | } 20 | 21 | .current-line-margin { 22 | background: rgba(190, 225, 255, 0.05); 23 | border: none !important; 24 | } 25 | 26 | .cursor { 27 | background: #c0c5ce !important; 28 | width: 1pt !important; 29 | } 30 | 31 | .monaco-editor .suggest-widget { 32 | color: #c0c5ce; 33 | background: #4e5d6c !important; 34 | border: none !important; 35 | box-shadow: 0px 12px 22px rgba(0,0,0,0.15); 36 | } 37 | 38 | .monaco-editor .view-overlays.focused .selected-text { 39 | background: rgba(100, 120, 160, 0.3) !important; 40 | } 41 | 42 | .monaco-editor .view-overlays .selected-text { 43 | background: rgba(100, 120, 160, 0.15) !important; 44 | } 45 | 46 | /* 47 | .monaco-editor .token.indent-guide { 48 | border-left: 1px solid #353535 !important; 49 | } 50 | */ 51 | .monaco-editor .monaco-scrollable-element .scrollbar.horizontal, 52 | .monaco-editor .decorationsOverviewRuler, 53 | .monaco-editor .monaco-scrollable-element .scrollbar.vertical .arrow-background { 54 | background: transparent; 55 | /*display: none;*/ 56 | } 57 | /* Make vertical scrollbar transparent to allow decorations overview ruler to be visible */ 58 | .monaco-editor .monaco-scrollable-element .scrollbar.vertical, 59 | .monaco-editor .monaco-scrollable-element .scrollbar.horizontal { 60 | background: transparent; 61 | visibility: hidden; 62 | /*display: none;*/ 63 | } 64 | 65 | .monaco-editor .monaco-scrollable-element .slider { 66 | background: #111 !important; 67 | border: 1pt solid #485360; 68 | border-radius: 100pt; 69 | } 70 | 71 | .errorDecoration { 72 | left: 0px !important; 73 | width: 100% !important; 74 | color: red; 75 | background: -webkit-linear-gradient(left, rgba(255, 0, 0, 0) 0%, rgba(255, 0, 0, 0.3) 97%, #f00 97%, #f00 100%) no-repeat border-box border-box !important; 76 | background: -moz-linear-gradient(left, rgba(255, 0, 0, 0) 0%, rgba(255, 0, 0, 0.3) 97%, #f00 97%, #f00 100%) no-repeat border-box border-box !important; 77 | background: linear-gradient(to left, rgba(255, 0, 0, 0) 0%, rgba(255, 0, 0, 0.3) 97%, #f00 97%, #f00 100%) no-repeat border-box border-box !important; 78 | background-size: cover !important; 79 | } 80 | -------------------------------------------------------------------------------- /scripts/custom-config.js: -------------------------------------------------------------------------------- 1 | const rewire = require('rewire'); 2 | const proxyquire = require('proxyquire'); 3 | 4 | switch (process.argv[2]) { 5 | // The "start" script is run during development mode 6 | case 'start': 7 | rewireModule('react-scripts/scripts/start.js', loadCustomizer('../webpack.config.dev.extension')); 8 | break; 9 | 10 | // The "build" script is run to produce a production bundle 11 | case 'build': 12 | rewireModule('react-scripts/scripts/build.js', loadCustomizer('../webpack.config.prod.extension')); 13 | break; 14 | 15 | // The "test" script runs all the tests with Jest 16 | case 'test': 17 | let customizer = loadCustomizer('../webpack.config.testing.extension'); 18 | proxyquire('react-scripts/scripts/test.js', { 19 | // When test.js asks for '../utils/createJestConfig' it will get this instead: 20 | '../utils/createJestConfig': (...args) => { 21 | // Use the existing createJestConfig function to create a config, then pass 22 | // it through the customizer 23 | var createJestConfig = require('react-scripts/utils/createJestConfig'); 24 | return customizer(createJestConfig(...args)); 25 | } 26 | }); 27 | break; 28 | 29 | default: 30 | console.log('custom-config only supports "start", "build", and "test" options.'); 31 | process.exit(-1); 32 | } 33 | 34 | // Attempt to load the given module and return null if it fails. 35 | function loadCustomizer(module) { 36 | try { 37 | return require(module); 38 | } catch (e) { 39 | if (e.code !== "MODULE_NOT_FOUND") throw e; 40 | } 41 | 42 | // If the module doesn't exist, return a 43 | // noop that simply returns the config it's given. 44 | return config => config; 45 | } 46 | 47 | function rewireModule(modulePath, customizer) { 48 | // Load the module with `rewire`, which allows modifying the 49 | // script's internal variables. 50 | //let defaults = rewire(modulePath); 51 | 52 | // Reach into the module, grab its global 'config' variable, 53 | // pass it through the customizer function, and then set it back. 54 | // 'config' is Create React App's built-in Webpack config. 55 | /*let config = defaults.__get__('config'); 56 | config = customizer(Object.assign({}, config)); 57 | defaults.__set__('config', config);*/ 58 | 59 | const patching = () => { 60 | var defaults = rewire(modulePath); 61 | var config = defaults.__get__('config'); 62 | return customizer(Object.assign({}, config)); 63 | }; 64 | 65 | proxyquire.noCallThru()(modulePath, { 66 | '../config/webpack.config.dev': patching(), 67 | '../config/webpack.config.prod': patching() 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /src/Components/SettingsButton.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import DropdownButton from 'react-bootstrap/lib/DropdownButton'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 6 | 7 | import ToggledOption from './ToggledOption'; 8 | import tooltip from './Tooltip'; 9 | 10 | import { CompilerDescriptions, CompilerList } from '../Common/Common'; 11 | 12 | export default class SettingsButton extends PureComponent { 13 | 14 | static defaultProps = { 15 | compiler: CompilerList[1], 16 | onOptionChange: () => {}, 17 | overlayTriggers: ['hover'], 18 | dropdownTitleElement: , 19 | dropdownStyle: { 20 | paddingLeft: '0.9em', 21 | paddingRight: '0.9em', 22 | }, 23 | } 24 | 25 | render() { 26 | const { 27 | compiler, 28 | onOptionChange, 29 | overlayTriggers, 30 | dropdownTitleElement, 31 | dropdownStyle, 32 | } = this.props; 33 | 34 | const options = { 35 | ...CompilerDescriptions[compiler].options, 36 | base64: { 37 | label: 'Base64', 38 | default: false, 39 | }, 40 | }; 41 | 42 | return ( 43 | 49 | 60 | { Object.keys(options).map(key => { 61 | const option = options[key]; 62 | if (!option || !option.label) 63 | return null; 64 | 65 | return ( 66 | onOptionChange(key, value) } 72 | /> 73 | ); 74 | }) } 75 | 76 | 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Grammars/theme.js: -------------------------------------------------------------------------------- 1 | 2 | export default function registerTheme(monaco) { 3 | let rules = { 4 | 'comment': '65737e', 5 | 'identifier': 'c0c5ce', 6 | 'delimiter': 'c0c5ce', 7 | 8 | 'variable.parameter.function': 'c0c5ce', 9 | 'punctuation.definition.variable': 'c0c5ce', 10 | 'punctuation.definition.parameters': 'c0c5ce', 11 | 'punctuation.definition.string': 'c0c5ce', 12 | 'punctuation.definition.array': 'c0c5ce', 13 | 'punctuation.separator.dot': 'f2777a', 14 | 15 | 'keyword': 'cc99cc', 16 | 'keyword.operator': 'f69057', 17 | 'keyword.call': '48c7e0', 18 | 'keyword.call_indirect': '48c7e0', 19 | 'keyword.call_import': '48c7e0', 20 | 21 | 'variable': '6699cc', 22 | 'variable.other.dollar': 'bf616a', 23 | 24 | 'entity.name.function': '8fa1b3', 25 | 'meta.require': '8fa1b3', 26 | 'support.function.any-method': '8fa1b3', 27 | 'variable.function': '8fa1b3', 28 | 29 | 'support.class': 'ebcb8b', 30 | 'entity.name.class': 'ebcb8b', 31 | 'entity.name.type.class': 'ebcb8b', 32 | 33 | 'meta.class': 'eff1f5', 34 | 'keyword.other.special-method': '8fa1b3', 35 | 36 | 'storage': 'cc99cc', 37 | 'support.function': '96b5b4', 38 | 39 | 'string': '81c56c', 40 | 'constant.other.symbol': '81c56c', 41 | 'entity.other.inherited-class': '81c56c', 42 | 43 | 'number': 'f2777a', 44 | 45 | 'entity.name.tag': 'bf616a', 46 | 'entity.other.attribute-name': 'f2777a', 47 | 'meta.selector': 'c0c5ce', 48 | 49 | 'keyword.other.unit': 'f2777a', 50 | 'string.other.link': 'bf616a', 51 | 52 | 'meta.link': 'f2777a', 53 | 54 | 'string.regexp': '96b5b4', 55 | 'constant.character.escape': '96b5b4', 56 | 57 | 'punctuation.section.embedded': 'ab7967', 58 | 'variable.interpolation': 'ab7967', 59 | 60 | 'invalid.illegal': ['2b303b', 'bf616a'], 61 | }; 62 | 63 | rules = Object.entries(rules).map(([token, props]) => { 64 | const hasArray = Array.isArray(props); 65 | return { 66 | token, 67 | foreground: hasArray ? props[0] : props, 68 | background: hasArray ? props[1] : void 0, 69 | } 70 | }); 71 | 72 | monaco.editor.defineTheme('vs-assembleash', { base: 'vs-dark', inherit: true, rules }); 73 | } 74 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | 2 | .fork-me { 3 | margin-right: 16px; 4 | margin-top: 5px; 5 | padding: 0; 6 | } 7 | 8 | .fork-me .github-octicon-mark { 9 | fill: #999; 10 | width: 42px; 11 | height: 42px; 12 | -moz-transition: fill .2s ease-in; 13 | -o-transition: fill .2s ease-in; 14 | -webkit-transition: fill .2s ease-in; 15 | transition: fill .2s ease-in; 16 | } 17 | 18 | .fork-me .github-octicon-mark:hover { 19 | fill: #fff; 20 | } 21 | 22 | .fork-me a { 23 | position: relative; 24 | display: block; 25 | width: 100%; 26 | height: 100%; 27 | outline: none; 28 | } 29 | 30 | .logo { 31 | margin-top: -0.9em; 32 | margin-right: 2rem; 33 | text-align: center; 34 | } 35 | 36 | .icon-binary-code:before { 37 | -webkit-font-smoothing: antialiased; 38 | content: "\e900"; 39 | color: #fff; 40 | } 41 | 42 | .Resizer { 43 | background: transparent; 44 | margin-bottom: 160px; 45 | z-index: 1; 46 | -moz-box-sizing: border-box; 47 | -webkit-box-sizing: border-box; 48 | box-sizing: border-box; 49 | -moz-background-clip: padding; 50 | -webkit-background-clip: padding; 51 | background-clip: padding-box; 52 | } 53 | 54 | .Resizer:hover { 55 | -webkit-transition: all 0.1s ease; 56 | transition: all 0.1s ease; 57 | } 58 | 59 | .Resizer.vertical { 60 | width: 5px; 61 | background: rgba(255, 255, 255, 0); 62 | cursor: col-resize; 63 | } 64 | 65 | .Resizer.vertical:active, 66 | .Resizer.vertical:hover { 67 | background: rgba(255, 255, 255, 0.1); 68 | } 69 | 70 | .Resizer.disabled { 71 | cursor: not-allowed; 72 | } 73 | 74 | .Resizer.disabled:hover { 75 | border-color: transparent; 76 | } 77 | 78 | .gly-spin { 79 | -webkit-animation: spin 2s infinite linear; 80 | -moz-animation: spin 2s infinite linear; 81 | animation: spin 2s infinite linear; 82 | } 83 | 84 | .busy-signal { 85 | position: relative; 86 | top: 15px; 87 | left: 26px; 88 | width: 26px; 89 | height: 26px; 90 | } 91 | 92 | .busy-success-color { 93 | color: #5bb85d; 94 | border-color: #5bb85d; 95 | } 96 | 97 | .busy-failure-color { 98 | color: #ff6836; 99 | border-color: #ff6836; 100 | } 101 | 102 | .busy-signal .busy-signal-symbol { 103 | font-size: 10px; 104 | position: relative; 105 | text-align: center; 106 | line-height: 24px; 107 | } 108 | 109 | .busy-signal .busy-signal-maskedCircle { 110 | width: 18px; 111 | height: 18px; 112 | border-radius: 10px; 113 | border: 1pt solid; 114 | box-sizing: border-box; 115 | } 116 | 117 | .busy-signal .busy-signal-mask { 118 | width: 10px; 119 | height: 10px; 120 | overflow: hidden; 121 | } 122 | 123 | .busy-signal .busy-signal-spinner { 124 | position: absolute; 125 | left: 1px; 126 | top: 1px; 127 | width: 24px; 128 | height: 24px; 129 | animation: spin 700ms infinite linear; 130 | } 131 | 132 | 133 | @-webkit-keyframes spin { 134 | from { -webkit-transform: rotate(0deg); } 135 | to { -webkit-transform: rotate(360deg); } 136 | } 137 | 138 | @keyframes spin { 139 | from { transform: rotate(0deg); } 140 | to { transform: rotate(360deg); } 141 | } 142 | -------------------------------------------------------------------------------- /src/toggle.button.css: -------------------------------------------------------------------------------- 1 | .react-toggle { 2 | float: right; 3 | touch-action: pan-x; 4 | 5 | display: inline-block; 6 | position: relative; 7 | cursor: pointer; 8 | background-color: transparent; 9 | border: 0; 10 | padding: 0; 11 | 12 | -webkit-touch-callout: none; 13 | -webkit-user-select: none; 14 | -khtml-user-select: none; 15 | -moz-user-select: none; 16 | -ms-user-select: none; 17 | user-select: none; 18 | 19 | -webkit-tap-highlight-color: rgba(0,0,0,0); 20 | -webkit-tap-highlight-color: transparent; 21 | } 22 | 23 | .react-toggle-screenreader-only { 24 | border: 0; 25 | clip: rect(0 0 0 0); 26 | height: 1px; 27 | margin: -1px; 28 | overflow: hidden; 29 | padding: 0; 30 | position: absolute; 31 | width: 1px; 32 | } 33 | 34 | .react-toggle--disabled { 35 | cursor: not-allowed; 36 | opacity: 0.5; 37 | -webkit-transition: opacity 0.25s; 38 | transition: opacity 0.25s; 39 | } 40 | 41 | .react-toggle-track { 42 | width: 57px; 43 | height: 32px; 44 | padding: 0; 45 | background-color: #2d3743; 46 | -webkit-transition: all 0.2s ease; 47 | -moz-transition: all 0.2s ease; 48 | transition: all 0.2s ease; 49 | } 50 | 51 | .react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track { 52 | background-color: #232a33; 53 | } 54 | 55 | .react-toggle--checked .react-toggle-track { 56 | background-color: #5cb85c; 57 | } 58 | 59 | .react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track { 60 | background-color: #3ca43c; 61 | } 62 | 63 | .react-toggle-track-check { 64 | position: absolute; 65 | width: 14px; 66 | height: 10px; 67 | top: 0px; 68 | bottom: 0px; 69 | margin-top: auto; 70 | margin-bottom: auto; 71 | line-height: 0; 72 | left: 8px; 73 | opacity: 0; 74 | -webkit-transition: opacity 0.25s ease; 75 | -moz-transition: opacity 0.25s ease; 76 | transition: opacity 0.25s ease; 77 | } 78 | 79 | .react-toggle--checked .react-toggle-track-check { 80 | opacity: 1; 81 | -webkit-transition: opacity 0.25s ease; 82 | -moz-transition: opacity 0.25s ease; 83 | transition: opacity 0.25s ease; 84 | } 85 | 86 | .react-toggle-track-x { 87 | position: absolute; 88 | width: 10px; 89 | height: 10px; 90 | top: 0px; 91 | bottom: 0px; 92 | margin-top: auto; 93 | margin-bottom: auto; 94 | line-height: 0; 95 | right: 10px; 96 | opacity: 1; 97 | -webkit-transition: opacity 0.25s ease; 98 | -moz-transition: opacity 0.25s ease; 99 | transition: opacity 0.25s ease; 100 | } 101 | 102 | .react-toggle-track-x svg path { 103 | fill: #4e5d6c; 104 | } 105 | 106 | .react-toggle--checked .react-toggle-track-x { 107 | opacity: 0; 108 | } 109 | 110 | .react-toggle-thumb { 111 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms; 112 | position: absolute; 113 | top: 2px; 114 | left: 2px; 115 | width: 28px; 116 | height: 28px; 117 | background-color: #4e5d6c; 118 | 119 | -webkit-box-sizing: border-box; 120 | -moz-box-sizing: border-box; 121 | box-sizing: border-box; 122 | 123 | -webkit-transition: all 0.25s ease; 124 | -moz-transition: all 0.25s ease; 125 | transition: all 0.25s ease; 126 | } 127 | 128 | .react-toggle--checked .react-toggle-thumb { 129 | left: 27px; 130 | } 131 | -------------------------------------------------------------------------------- /src/Components/CompileButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import SplitButton from 'react-bootstrap/lib/SplitButton'; 5 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 6 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 7 | import MenuItem from 'react-bootstrap/lib/MenuItem'; 8 | 9 | import tooltip from './Tooltip'; 10 | 11 | import { CompileMode, CompileModes } from '../Common/Common'; 12 | 13 | export default class CompileButton extends Component { 14 | static propTypes = { 15 | disabled: PropTypes.bool, 16 | onChange: PropTypes.func, 17 | onClick: PropTypes.func 18 | } 19 | 20 | static defaultProps = { 21 | disabled: true, 22 | onChange: () => {}, 23 | onClick: () => {} 24 | } 25 | 26 | constructor(props) { 27 | super(props); 28 | this.state = { 29 | isCompiling: false, 30 | compileMode: CompileMode.Auto, 31 | cursor: 'pointer' 32 | }; 33 | } 34 | 35 | startCompile() { 36 | this.setState({ 37 | isCompiling: true, 38 | cursor: 'wait' 39 | }); 40 | } 41 | 42 | endCompile() { 43 | this.setState({ 44 | isCompiling: false, 45 | cursor: 'pointer' 46 | }); 47 | } 48 | 49 | onCompile = () => { 50 | const { onClick } = this.props; 51 | onClick(this.state.compileMode); 52 | } 53 | 54 | onSelect = compileMode => { 55 | this.setState({ compileMode }); 56 | this.props.onChange(compileMode); 57 | } 58 | 59 | render() { 60 | const { disabled } = this.props; 61 | const { isCompiling, compileMode, cursor } = this.state; 62 | 63 | const title = ( 64 | 65 | { '\t' + CompileModes[compileMode] } 69 | 70 | ); 71 | 72 | return ( 73 | 79 | 92 | { 93 | CompileModes.map((value, index) => 94 | 100 |

{ value }

101 |
102 | ) 103 | } 104 |
105 |
106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Containers/ToolbarContainer.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import ButtonGroup from 'react-bootstrap/lib/ButtonGroup'; 6 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 7 | import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar'; 8 | 9 | import CompilerButton from '../Components/CompilerButton'; 10 | import AboutButton from '../Components/AboutButton'; 11 | import CompileButton from '../Components/CompileButton'; 12 | import SettingsButton from '../Components/SettingsButton'; 13 | import OutputButtonGroup from '../Components/OutputButtonGroup'; 14 | 15 | import tooltip from '../Components/Tooltip'; 16 | 17 | const OctocatIcon = props => ( 18 | 24 | ); 25 | 26 | export default class ToolbarContainer extends Component { 27 | static propTypes = { 28 | version: PropTypes.string, 29 | compiler: PropTypes.string, 30 | compileDisabled: PropTypes.bool, 31 | 32 | onCompilerChange: PropTypes.func, 33 | onCompileClick: PropTypes.func, 34 | onCompileModeChange: PropTypes.func, 35 | onSettingsOptionChange: PropTypes.func, 36 | onOutputSelect: PropTypes.func 37 | } 38 | 39 | static defaultProps = { 40 | version: '0.0.0', 41 | compiler: 'Unknown', 42 | compileDisabled: false, 43 | 44 | onCompilerChange: () => {}, 45 | onCompileClick: () => {}, 46 | onCompileModeChange: () => {}, 47 | onSettingsOptionChange: () => {}, 48 | onOutputSelect: () => {}, 49 | 50 | overlayTriggers: ['hover', 'focus'], 51 | } 52 | 53 | constructor(props) { 54 | super(props); 55 | this.state = { 56 | compiler: props.compiler, 57 | } 58 | } 59 | 60 | getCompileButtonRef = self => { 61 | this.compileButton = self; 62 | } 63 | 64 | render() { 65 | const { compiler } = this.state; 66 | const { 67 | version, 68 | compileDisabled, 69 | onSettingsOptionChange, 70 | onCompilerChange, 71 | onCompileModeChange, 72 | onCompileClick, 73 | onOutputSelect, 74 | overlayTriggers, 75 | } = this.props; 76 | 77 | return ( 78 | 79 | 80 | { 83 | this.setState({ compiler }); 84 | onCompilerChange(compiler); 85 | }} 86 | /> 87 | 88 | 89 | 90 | 96 | 97 | 98 | 102 | 103 | 104 | 105 |
106 |

Assembleash BETA

107 |
108 | 109 | 115 |
116 | 117 | 118 | 119 |
120 |
121 |
122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Containers/FooterContainer.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { PureComponent } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import Button from 'react-bootstrap/lib/Button'; 6 | import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar'; 7 | import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 8 | 9 | import BusySignal from '../Components/BusySignal'; 10 | 11 | export default class FooterContainer extends PureComponent { 12 | static propTypes = { 13 | downloadDisabled: PropTypes.bool, 14 | onDownloadPressed: PropTypes.func, 15 | errorMessage: PropTypes.string, 16 | cursorPosition: PropTypes.arrayOf(PropTypes.number, PropTypes.number) 17 | } 18 | 19 | static defaultProps = { 20 | busyState: 'pending', 21 | downloadDisabled: true, 22 | errorMessage: null, 23 | cursorPosition: [0, 0], 24 | onDownloadPressed: () => {}, 25 | } 26 | 27 | formatCursorPosition = pos => `${ pos[0] }:${ pos[1] }` 28 | 29 | render() { 30 | const { 31 | binarySize, 32 | onDownloadPressed, 33 | downloadDisabled, 34 | busyState, 35 | cursorPosition, 36 | errorCount, 37 | errorMessage, 38 | } = this.props; 39 | 40 | const sizeUnits = binarySize.split(' '); 41 | const size = sizeUnits[0] || ''; 42 | const unit = sizeUnits[1] || ''; 43 | 44 | let statusBarMessage = '', 45 | messageClass = 'busy-success-color'; 46 | 47 | switch (busyState) { 48 | case 'pending': statusBarMessage = 'Processing...'; break; 49 | case 'success': statusBarMessage = 'Compiled successfully'; break; 50 | case 'failure': 51 | messageClass = 'busy-failure-color'; 52 | statusBarMessage = `(${ errorCount }) Error${ errorCount > 1 ? 's' : '' }`; 53 | if (errorMessage) statusBarMessage += ': ' + errorMessage; 54 | break; 55 | default: break; 56 | } 57 | 58 | return ( 59 | 67 | 82 | 83 |
84 |

{ size } 85 | 86 | { ' ' + unit } 87 | 88 |

89 |
90 | 91 | 107 | 108 |
114 | 118 | 119 | 136 |
137 |
138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Common/Common.js: -------------------------------------------------------------------------------- 1 | import Base64 from './Base64'; 2 | 3 | export const CompileMode = { 4 | Auto: 0, 5 | Manual: 1, 6 | }; 7 | 8 | export const CompileModes = Object.keys(CompileMode); 9 | 10 | export const CompilerDescriptions = { 11 | 'AssemblyScript': { 12 | offline: true, 13 | loaded: false, 14 | github: 'https://github.com/AssemblyScript/assemblyscript', 15 | 16 | options: { 17 | longMode: { 18 | label: 'Use 64 bits', 19 | default: false, 20 | }, 21 | 22 | validate: { 23 | label: 'Validate', 24 | default: true, 25 | }, 26 | 27 | optimize: { 28 | label: 'Optimize', 29 | default: true, 30 | } 31 | }, 32 | 33 | example:` 34 | export function fib(n: i32): i32 { 35 | if (n <= 1) return 1; 36 | return fib(n - 1) + fib(n - 2); 37 | }` 38 | }, 39 | 40 | 'Speedy.js': { 41 | offline: false, 42 | url: 'https://speedyjs-saas.herokuapp.com', 43 | github: 'https://github.com/MichaReiser/speedy.js', 44 | 45 | options: { 46 | unsafe: { 47 | label: 'Unsafe', 48 | default: true 49 | }, 50 | 51 | optimize: { 52 | label: 'Optimize', 53 | default: true, 54 | }, 55 | }, 56 | 57 | version: () => requestCommand( 58 | CompilerDescriptions['Speedy.js'].url + '/version' 59 | ), 60 | 61 | compile: (source, options) => { 62 | const requestBody = { 63 | files: [{ 64 | source, 65 | fileName: 'module.ts', 66 | }], 67 | tsconfig: options, 68 | }; 69 | 70 | return requestCommand( 71 | CompilerDescriptions['Speedy.js'].url + '/compile', 72 | requestBody 73 | ); 74 | }, 75 | 76 | example: ` 77 | function fibSync(n: int): int { 78 | "use speedyjs"; 79 | if (n <= 1) return 1; 80 | return fibSync(n - 1) + fibSync(n - 2); 81 | } 82 | 83 | async function fib(n: int): Promise { 84 | "use speedyjs"; 85 | return fibSync(n); 86 | } 87 | ` 88 | } 89 | }; 90 | 91 | export const CompilerList = Object.keys(CompilerDescriptions); 92 | 93 | const LibStdKeywords = [ 94 | 'new', 95 | 'malloc', 96 | 'free', 97 | 'memcpy', 98 | 'memset', 99 | 'memcmp', 100 | ]; 101 | 102 | const LibStdKeywordsRegex = new RegExp(LibStdKeywords.join("|"), "gm"); 103 | 104 | export function isRequreStdlib(code) { 105 | LibStdKeywordsRegex.lastIndex = 0; 106 | return LibStdKeywordsRegex.test(code); 107 | } 108 | 109 | export function anyExists(array, value) { 110 | const findFunc = 111 | Array.isArray(value) 112 | ? v => value.indexOf(v) >= 0 113 | : v => value === v; 114 | 115 | return array.some(findFunc); 116 | } 117 | 118 | export function getCompilerVersion(compiler, callback = () => {}) { 119 | switch (compiler) { 120 | 121 | case 'AssemblyScript NEXT': 122 | if (window.assemblyscript) 123 | callback(window.assemblyscript.version || '0.5.0'); 124 | break; 125 | 126 | case 'Speedy.js': 127 | CompilerDescriptions['Speedy.js'] 128 | .version() 129 | .then(version => callback(version['speedyjs-compiler'])) 130 | .catch(err => callback('0.0.0')); 131 | break; 132 | 133 | default: 134 | callback('0.0.1'); 135 | } 136 | } 137 | 138 | export function formatCode(buffer, base64 = false) { 139 | if (!buffer) 140 | return ''; 141 | 142 | // buffer already formatted 143 | if (typeof buffer === 'string') 144 | return buffer; 145 | 146 | if (base64) { 147 | let output = `const byteArray = decode('${ Base64.encode(buffer) }');\n\n`; 148 | 149 | output += 'function decode(input) {\n'; 150 | output += ' if (typeof window !== \'undefined\' && atob in window) {\n'; 151 | output += ' return new Uint8Array(atob(input).split(\'\').map(char => char.charCodeAt(0)));\n'; 152 | output += ' } else if (typeof Buffer !== \'undefined\') {\n'; 153 | output += ' return Buffer.from(input, \'base64\');\n'; 154 | output += ' }\n'; 155 | output += ' return null;\n'; 156 | output += '}'; 157 | 158 | return output; 159 | } 160 | 161 | // format binary data as array 162 | const last = buffer.length; 163 | 164 | let output = 'new Uint8Array([\n '; 165 | for (let i = 0, len = buffer.length; i < len; i++) { 166 | const value = buffer[i]; 167 | // let result = '0x' + ('00' + value.toString(16)).substr(-2); 168 | let result = value.toString(); 169 | 170 | if (i !== last - 1) 171 | result += ', '; 172 | 173 | if (((i + 1) & 15) === 0) 174 | result += '\n '; 175 | 176 | output += result; 177 | } 178 | output += '\n]);'; 179 | return output; 180 | } 181 | 182 | export function formatSize(bytes) { 183 | if (!bytes) return '0 Bytes'; 184 | const units = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 185 | let i = Math.floor(Math.log(bytes) * (1 / Math.log(1024))); 186 | return Math.round(bytes * Math.pow(1024, -i) * 100) / 100 + ' ' + units[i]; 187 | } 188 | 189 | function checkResponseStatus(response) { 190 | if (response.status >= 200 && response.status < 300) 191 | return Promise.resolve(response); 192 | else 193 | return Promise.reject(new Error(response.statusText)); 194 | } 195 | 196 | export function requestCommand(url, config = null) { 197 | const headers = config ? { 198 | 'Accept': 'application/json', 199 | 'Content-Type': 'application/json', 200 | } : void 0; 201 | 202 | return fetch(url, { 203 | headers, 204 | method: config ? 'POST' : 'GET', 205 | body: config ? JSON.stringify(config) : void 0, 206 | }) 207 | .then(checkResponseStatus) 208 | .then(response => response.json()); 209 | } 210 | -------------------------------------------------------------------------------- /src/Grammars/wast.js: -------------------------------------------------------------------------------- 1 | 2 | // Based on https://github.com/dcodeIO/AssemblyScript/blob/gh-pages/assets/sexpr.js 3 | 4 | export default function registerWastSyntax(monaco) { 5 | monaco.languages.register({ id: 'wast' }); 6 | 7 | monaco.languages.setLanguageConfiguration('wast', { 8 | comments: { 9 | lineComment: ';;', 10 | blockComment: ['\\(;', ';\\)'], 11 | }, 12 | brackets: [ 13 | ['(', ')'] 14 | ], 15 | autoClosingPairs: [ 16 | { open: '(', close: ')' }, 17 | { open: '"', close: '"' } 18 | ], 19 | surroundingPairs: [ 20 | { open: '(', close: ')' }, 21 | { open: '"', close: '"' } 22 | ] 23 | }); 24 | 25 | monaco.languages.setMonarchTokensProvider('wast', { 26 | 27 | tokenPostfix: '.wast', 28 | 29 | keywords: [ 30 | "module", 31 | "type", 32 | "memory", 33 | "data", 34 | "export", 35 | "import", 36 | "func", 37 | "struct", 38 | "start", 39 | "table", 40 | "elem", 41 | "element", 42 | "global", 43 | "mut", 44 | "local", 45 | "param", 46 | "result", 47 | "offset", 48 | "code", 49 | "segment", 50 | "invoke", 51 | "label", 52 | "binary", 53 | "shared", 54 | "notshared", 55 | 56 | // Control 57 | "unreachable", 58 | "nop", 59 | "block", 60 | "loop", 61 | "if", 62 | "else", 63 | "if_else", 64 | "end", 65 | "br", 66 | "br_if", 67 | "br_table", 68 | "return", 69 | 70 | // Call 71 | "call", 72 | "call_indirect", 73 | "call_import" 74 | ], 75 | 76 | types: [ 77 | "i32", 78 | "i64", 79 | "f32", 80 | "f64", 81 | "anyfunc", 82 | "func", 83 | "void" 84 | ], 85 | 86 | operations: [ 87 | // Parametric 88 | "drop", 89 | "select", 90 | 91 | // Variable access 92 | "get_local", 93 | "set_local", 94 | "tee_local", 95 | "get_global", 96 | "set_global", 97 | 98 | // Memory related 99 | "load", 100 | "load8_s", 101 | "load8_u", 102 | "load16_s", 103 | "load16_u", 104 | "load32_s", 105 | "load32_u", 106 | "store", 107 | "store8", 108 | "store16", 109 | "store32", 110 | "current_memory", 111 | "grow_memory", 112 | 113 | // Constants 114 | "const", 115 | 116 | // Comparison 117 | "eqz", 118 | "eq", 119 | "ne", 120 | "lt_s", 121 | "lt_u", 122 | "lt", 123 | "gt_s", 124 | "gt_u", 125 | "gt", 126 | "le_s", 127 | "le_u", 128 | "le", 129 | "ge_s", 130 | "ge_u", 131 | "ge", 132 | 133 | // Numeric 134 | "clz", 135 | "ctz", 136 | "popcnt", 137 | "add", 138 | "sub", 139 | "mul", 140 | "div_s", 141 | "div_u", 142 | "div", 143 | "rem_s", 144 | "rem_u", 145 | "and", 146 | "xor", 147 | "or", 148 | "shl", 149 | "shr_s", 150 | "shr_u", 151 | "rotl", 152 | "rotr", 153 | "abs", 154 | "neg", 155 | "ceil", 156 | "floor", 157 | "trunc", 158 | "nearest", 159 | "sqrt", 160 | "min", 161 | "max", 162 | "copysign", 163 | 164 | // Conversion 165 | "wrap", 166 | "trunc_s", 167 | "trunc_u", 168 | "extend_s", 169 | "extend_u", 170 | "convert_s", 171 | "convert_u", 172 | "demote", 173 | "promote", 174 | 175 | // Reinterpretations 176 | "reinterpret", 177 | 178 | // Misc 179 | "tableswitch", 180 | "has_feature", 181 | 182 | // Assertion 183 | "failure", 184 | "assert_invalid", 185 | "assert_trap", 186 | "assert_return", 187 | "assert_return_nan", 188 | "assert_return_arithmetic_nan", 189 | "assert_return_canonical_nan", 190 | "assert_unlinkable" 191 | ], 192 | 193 | escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/, 194 | digits: /\d+(_+\d+)*/, 195 | octaldigits: /[0-7]+(_+[0-7]+)*/, 196 | binarydigits: /[0-1]+(_+[0-1]+)*/, 197 | hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/, 198 | 199 | tokenizer: { 200 | root: [ 201 | { include: '@whitespace' }, 202 | 203 | [/"([^"\\]|\\.)*$/, 'string.invalid'], 204 | [/"/, 'string', '@string'], 205 | 206 | [/(@digits)[eE]([+-]?(@digits))?[fFdD]?/, 'number.float'], 207 | [/(@digits)\.(@digits)([eE][+-]?(@digits))?[fFdD]?/, 'number.float'], 208 | [/0[xX](@hexdigits)[Ll]?/, 'number.hex'], 209 | [/0(@octaldigits)[Ll]?/, 'number.octal'], 210 | [/0[bB](@binarydigits)[Ll]?/, 'number.binary'], 211 | [/(@digits)[fFdD]/, 'number.float'], 212 | [/(@digits)[lL]?/, 'number'], 213 | [/[+-]?(infinity|inf|nan)/, 'constant.number'], 214 | 215 | [/\./, 'punctuation.separator.dot'], 216 | 217 | [/\$[^\s"(){}[\]]+/, { token: 'variable' }], 218 | 219 | [/[a-zA-Z_$][\w$]*/, { 220 | cases: { 221 | '@keywords': { token: 'keyword.$0' }, 222 | '@types': { token: 'entity.name.type.class.$0' }, 223 | '@operations': { token: 'keyword.operator.$0' }, 224 | '@default': 'identifier' 225 | } 226 | }] 227 | ], 228 | 229 | string: [ 230 | [/[^\\"]+/, 'string'], 231 | [/@escapes/, 'string.escape'], 232 | [/\\./, 'string.escape.invalid'], 233 | [/"/, 'string', '@pop'] 234 | ], 235 | 236 | whitespace: [ 237 | [/[ \t\r\n]+/, ''], 238 | [/(^;{2}.*$)/, 'comment.line'], 239 | [/(^\(;[\n\t\r\w]+;\))/, 'comment.block'] 240 | ], 241 | 242 | comment: [ 243 | [/(;{2})(.*)$/, 'comment.line'] 244 | ] 245 | } 246 | }); 247 | } 248 | -------------------------------------------------------------------------------- /src/Components/Editor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import MonacoEditor from 'react-monaco-editor'; 5 | import registerTheme from '../Grammars/theme'; 6 | import registerWastSyntax from '../Grammars/wast'; 7 | 8 | export default class Editor extends Component { 9 | static wastRegistered = false 10 | 11 | static propTypes = { 12 | focus: PropTypes.bool, 13 | readOnly: PropTypes.bool, 14 | mode: PropTypes.string, 15 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 16 | height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 17 | code: PropTypes.string, 18 | annotations: PropTypes.array, 19 | onChange: PropTypes.func, 20 | 21 | onPositionChange: PropTypes.func 22 | } 23 | 24 | static defaultProps = { 25 | focus: false, 26 | readOnly: false, 27 | mode: 'typescript', 28 | width: '100%', 29 | height: '750px', 30 | code: '', 31 | annotations: [], 32 | onChange: () => {}, 33 | 34 | onPositionChange: () => {} 35 | } 36 | 37 | constructor(props) { 38 | super(props); 39 | this.state = { 40 | value: props.code 41 | }; 42 | 43 | this.decorations = []; 44 | } 45 | 46 | componentWillReceiveProps(nextProps) { 47 | if (this.editor) { 48 | if (nextProps.mode !== this.props.mode) { 49 | console.log('mode:', nextProps.mode); 50 | this.editor.getModel().updateOptions({ 51 | tabSize: nextProps.mode === 'wast' ? 1 : 4 52 | }); 53 | 54 | } 55 | 56 | if (nextProps.code !== this.props.code) { 57 | this.editor.setValue(nextProps.code); 58 | } 59 | 60 | if (nextProps.width !== this.props.width || 61 | nextProps.height !== this.props.height) { 62 | const width = nextProps.width || this.props.width; 63 | const height = nextProps.height || this.props.height; 64 | this.editor.layout({ width, height }); 65 | } 66 | 67 | if (nextProps.annotations !== this.props.annotations) { 68 | let decorations = []; 69 | if (nextProps.annotations.length > 0) { 70 | const annotations = nextProps.annotations; 71 | 72 | for (let annotation of annotations) { 73 | decorations.push({ 74 | range: new Editor.monaco.Range(annotation.row, 1, annotation.row), 75 | options: { 76 | isWholeLine: false, 77 | linesDecorationsClassName: 'errorDecoration', 78 | glyphMarginHoverMessage: annotation.text 79 | } 80 | }); 81 | } 82 | } 83 | 84 | this.decorations = this.editor.deltaDecorations(this.decorations, decorations); 85 | } 86 | } 87 | } 88 | 89 | static addExtraLibs() { 90 | if (this.monaco && !this.extraLibsRegistered) { 91 | this.extraLibsRegistered = true; 92 | 93 | if (window.assemblyscript) { 94 | fetch('https://raw.githubusercontent.com/AssemblyScript/assemblyscript/master/std/assembly/index.d.ts') 95 | .then(response => response.text()) 96 | .then(asDefinisionFile => { 97 | const ts = this.monaco.languages.typescript; 98 | ts.typescriptDefaults.addExtraLib(asDefinisionFile, 'assembly.d.ts'); 99 | }) 100 | .catch(e => console.error('Can\'t get assembly script definision file:', e)); 101 | /*if (window.assemblyscript.library) { 102 | const files = window.assemblyscript.library.files; 103 | const names = Object.keys(files); 104 | 105 | const typescript = this.monaco.languages.typescript; 106 | for (let index = 0, len = names.length; index < len; index++) 107 | typescript.typescriptDefaults.addExtraLib(files[names[index]], names[index]); 108 | }*/ 109 | 110 | 111 | } 112 | } 113 | } 114 | 115 | replaceTextInRange(range, text) { 116 | const replaceOperation = { 117 | text, 118 | range, 119 | identifier: { major: 1, minor: 1 }, 120 | forceMoveMarkers: true 121 | }; 122 | this.editor.executeEdits("replace", [replaceOperation]); 123 | } 124 | 125 | onLoad = (editor, monaco) => { 126 | this.editor = editor; 127 | Editor.monaco = monaco; 128 | 129 | Editor.addExtraLibs(); 130 | 131 | if (!Editor.wastRegistered) { 132 | Editor.wastRegistered = true; 133 | 134 | const typescript = window.monaco.languages.typescript; 135 | typescript.typescriptDefaults.setCompilerOptions({ 136 | target: typescript.ScriptTarget.Latest, 137 | module: typescript.ModuleKind.None, 138 | noLib: true, 139 | allowNonTsExtensions: true 140 | }); 141 | 142 | registerWastSyntax(window.monaco); 143 | registerTheme(window.monaco); 144 | } 145 | 146 | this.editor.getModel().updateOptions({ 147 | tabSize: this.props.mode === 'wast' ? 1 : 4, 148 | }); 149 | 150 | // this.editor.updateOptions({ 151 | // theme: 'vs-assembleash' 152 | // }); 153 | 154 | this.editor.onDidChangeCursorPosition(e => { 155 | this.props.onPositionChange([e.position.lineNumber, e.position.column]); 156 | }) 157 | 158 | if (this.props.focus) { 159 | editor.focus(); 160 | } 161 | 162 | // TEST 163 | /*editor.deltaDecorations([], [{ 164 | range: new monaco.Range(2,1, 2), 165 | options: { 166 | isWholeLine: false, 167 | linesDecorationsClassName: 'errorDecoration', 168 | glyphMarginHoverMessage: 'error TS10234: Bla bla' 169 | } 170 | }]);*/ 171 | } 172 | 173 | onChange = newValue => { 174 | this.setState({ value: newValue }); 175 | this.props.onChange(newValue); 176 | } 177 | 178 | render() { 179 | const { value } = this.state; 180 | const { 181 | width, 182 | height, 183 | mode, 184 | readOnly, 185 | code 186 | } = this.props; 187 | 188 | const text = !readOnly ? value : code; 189 | const fontSize = 14; 190 | 191 | return ( 192 | 235 | ); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Assembleash Playground 23 | 24 | 25 | 26 | 27 | 28 | 29 | 210 | 211 | 212 | 213 | 242 | 243 | 244 | 245 | 246 | 249 | 250 | 251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | 263 |
264 | 265 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/Containers/EditorContainer.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import FileSaver from 'file-saver'; 7 | import { NotificationStack } from 'react-notification'; 8 | import { OrderedSet } from 'immutable'; 9 | import SplitPane from 'react-split-pane'; 10 | import { throttle } from 'throttle-debounce'; 11 | 12 | import Editor from '../Components/Editor'; 13 | import ToolbarContainer from './ToolbarContainer'; 14 | import FooterContainer from './FooterContainer'; 15 | 16 | import { 17 | CompileMode, 18 | CompilerDescriptions, 19 | formatCode, 20 | formatSize, 21 | getCompilerVersion, 22 | isRequreStdlib, 23 | } from '../Common/Common'; 24 | 25 | const AutoCompilationDelay = 800; //ms 26 | const MaxPrintingErrors = 8; 27 | const SplitGripWidth = 5; 28 | 29 | export default class EditorContainer extends Component { 30 | static defaultProps = { 31 | compiler: 'AssemblyScript' 32 | } 33 | 34 | static propTypes = { 35 | compiler: PropTypes.string 36 | } 37 | 38 | constructor(props) { 39 | super(props); 40 | this.state = { 41 | version: '0.0.0', 42 | compiler: props.compiler, 43 | compileMode: CompileMode.Auto, 44 | compilerReady: false, 45 | // compileFailure: false, 46 | compileState: 'pending', 47 | // compileSuccess: false, 48 | splitPosition: 0.62, 49 | additionalStatusMessage: '', 50 | inputEditorWidth: '100%', 51 | outputEditorWidth: '100%', 52 | editorsHeight: '750px', 53 | input: CompilerDescriptions[props.compiler].example.trim(), 54 | output: { 55 | text: '', 56 | binary: null 57 | }, 58 | outputType: 'text', 59 | 60 | // settings 61 | validate: true, 62 | optimize: true, 63 | longMode: false, 64 | unsafe: true, 65 | base64: false, 66 | 67 | annotations: OrderedSet(), 68 | notifications: OrderedSet(), 69 | notificationCount: 0, 70 | 71 | inputCursorPosition: [1, 1] 72 | }; 73 | 74 | this._errorCount = 0; 75 | this._lastTextInput = ''; 76 | this._compileTimerDelay = null; 77 | this._cachedClientRect = null; 78 | } 79 | 80 | componentDidMount() { 81 | this.updateWindowDimensions(); 82 | window.addEventListener("resize", this.updateWindowDimensions); 83 | this.changeCompiler(); 84 | } 85 | 86 | componentWillUnmount() { 87 | window.removeEventListener("resize", this.updateWindowDimensions); 88 | } 89 | 90 | updateWindowDimensions = () => { 91 | this._cachedClientRect = null; 92 | this.handleSize(); 93 | } 94 | 95 | _clearCompileTimeout() { 96 | this._compileTimerDelay && clearTimeout(this._compileTimerDelay); 97 | this._compileTimerDelay = null; 98 | } 99 | 100 | updateCompilationWithDelay = (delay = 5000) => { 101 | this._clearCompileTimeout(); 102 | this._compileTimerDelay = setTimeout(() => { 103 | this.updateCompilation(); 104 | this._compileTimerDelay = null; 105 | }, delay); 106 | } 107 | 108 | updateCompilation = () => { 109 | if (!this.inputEditor) return; 110 | 111 | //console.clear(); 112 | 113 | // clean errors and messages 114 | this._errorCount = 0; 115 | this.removeAllNotifications(); 116 | this.removeAllAnnotation(); 117 | this.setState({ 118 | additionalStatusMessage: '' 119 | }); 120 | 121 | const { 122 | compiler, 123 | longMode, 124 | validate, 125 | optimize, 126 | unsafe 127 | } = this.state; 128 | 129 | const inputCode = this.inputEditor.state.value; 130 | 131 | if (this.toolbar && this.toolbar.compileButton) { 132 | this.toolbar.compileButton.startCompile(); 133 | this.setState({ compileStatus: 'compiling' }); 134 | } 135 | 136 | setImmediate(() => { 137 | try { 138 | switch (compiler) { 139 | case 'AssemblyScript': 140 | const stdlib = isRequreStdlib(inputCode); 141 | this.compileByAssemblyScript(inputCode, { 142 | noMemory: !stdlib, 143 | longMode, 144 | validate, 145 | optimize 146 | }); 147 | break; 148 | 149 | case 'Speedy.js': 150 | this.compileBySpeedyJs(inputCode, { 151 | unsafe, 152 | optimizationLevel: optimize ? 3 : 0, 153 | saveWast: true 154 | }); 155 | break; 156 | 157 | default: console.warn('Compiler not supported'); 158 | } 159 | 160 | } catch (e) { 161 | 162 | this.setState({ compileStatus: 'failure' }); 163 | this._errorCount = 1; 164 | 165 | const message = '<' + compiler + '> internal error: '; 166 | this.addNotification(message + e.message); 167 | console.error(message, e); 168 | 169 | this.setState({ 170 | additionalStatusMessage: message + e.message 171 | }); 172 | 173 | } finally { 174 | if (this.toolbar && this.toolbar.compileButton) 175 | this.toolbar.compileButton.endCompile(); 176 | } 177 | }); 178 | } 179 | 180 | compileByAssemblyScript(code, { noMemory, longMode, validate, optimize }) { 181 | const as = window.assemblyscript; 182 | 183 | const options = as.createOptions(); 184 | 185 | as.setTarget(options, +longMode); 186 | as.setNoTreeShaking(options, false); 187 | as.setNoAssert(options, false); 188 | as.setNoMemory(options, noMemory); 189 | 190 | const parser = as.parseFile(code, 'index.ts', null, true); 191 | const module = as.compile(parser, options); 192 | 193 | const checkDiagnostics = (parser) => { 194 | let diagnostic, index = 0; 195 | let hasErrors = false; 196 | 197 | while ((diagnostic = as.nextDiagnostic(parser)) != null) { 198 | let errorMessage = as.formatDiagnostic(diagnostic, false, true); 199 | 200 | if (index <= MaxPrintingErrors) { 201 | console.error(errorMessage); 202 | this.addNotification(errorMessage); 203 | this.addAnnotation(errorMessage); 204 | } else { 205 | errorMessage = 'Too many errors'; 206 | console.error(errorMessage); 207 | this.addNotification(errorMessage); 208 | hasErrors = true; 209 | break; 210 | } 211 | 212 | if (as.isError(diagnostic)) { 213 | hasErrors = true; 214 | } 215 | 216 | index++; 217 | this._errorCount = index; 218 | } 219 | 220 | return hasErrors; 221 | } 222 | 223 | setImmediate(() => { 224 | if (!module || checkDiagnostics(parser)) { 225 | this.setState({ compileStatus: 'failure' }); 226 | return; 227 | } 228 | 229 | // hardcoded for now 230 | module.runPasses([ "trap-mode-clamp" ]); 231 | module.setOptimizeLevel(3); 232 | module.setShrinkLevel(0); 233 | 234 | setImmediate(() => { 235 | if (validate) { 236 | if (!module.validate()) { 237 | let notValid = 'Code validation error'; 238 | console.error(notValid); 239 | this.addNotification(notValid); 240 | this._errorCount = 1; 241 | this.setState({ 242 | compileStatus: 'failure', 243 | additionalStatusMessage: notValid 244 | }); 245 | module.dispose(); 246 | return; 247 | } 248 | } 249 | 250 | if (optimize) 251 | module.optimize(); 252 | 253 | this._errorCount = 0; 254 | 255 | setImmediate(() => { 256 | const text = module.toText(); 257 | const { 258 | output: binary, 259 | } = module.toBinary(); 260 | 261 | this.setState({ 262 | compileStatus: 'success', 263 | output: { text, binary }, 264 | }); 265 | 266 | module.dispose(); 267 | }); 268 | }); 269 | }); 270 | } 271 | 272 | compileBySpeedyJs(code, options) { 273 | CompilerDescriptions['Speedy.js'].compile(code, options) 274 | .then(response => { 275 | if (this.state.compiler !== 'Speedy.js') return; 276 | 277 | if (response.length) { 278 | const output = response[0]; 279 | if (output.exitStatus !== 0) { 280 | this.setState({ compileStatus: 'failure' }); 281 | 282 | // compiled failure 283 | const diagnostics = output.diagnostics; 284 | this._errorCount = diagnostics.length; 285 | 286 | for (let i = 0; i < diagnostics.length; i++) { 287 | let errorMessage = diagnostics[i]; 288 | 289 | if (i <= MaxPrintingErrors) { 290 | console.error(errorMessage); 291 | this.addNotification(errorMessage); 292 | this.addAnnotation(errorMessage); 293 | } else { 294 | errorMessage = `Too many errors (${diagnostics.length})`; 295 | console.error(errorMessage); 296 | this.addNotification(errorMessage); 297 | break; 298 | } 299 | } 300 | } else { 301 | 302 | this._errorCount = 0; 303 | 304 | // compiled successfully 305 | this.setState({ 306 | compileStatus: 'success', 307 | 308 | output: { 309 | text: output.wast || '', 310 | binary: new Uint8Array(output.wasm) 311 | } 312 | }); 313 | } 314 | } 315 | }) 316 | .catch(error => { 317 | this.setState({ compileStatus: 'failure' }); 318 | this._errorCount = 1; 319 | 320 | const message = '<' + this.state.compiler + '> Service not response'; 321 | this.addNotification(message); 322 | console.error(message); 323 | }); 324 | } 325 | 326 | onInputChange = value => { 327 | // skip compilation if possible 328 | value = value.trim(); 329 | if (this._lastTextInput !== value) { 330 | if (this.state.compileMode === CompileMode.Auto) 331 | this.updateCompilationWithDelay(AutoCompilationDelay); 332 | this._lastTextInput = value; 333 | } 334 | } 335 | 336 | onInputPositionChange = inputCursorPosition => { 337 | this.setState({ inputCursorPosition }); 338 | } 339 | 340 | onDownloadBinary = () => { 341 | const { output, compiler } = this.state; 342 | var blob = new Blob([output.binary], { type: "application/octet-stream" }); 343 | FileSaver.saveAs(blob, `${compiler.toLowerCase()}.module.wasm`); 344 | } 345 | 346 | changeCompiler = compiler => { 347 | this._errorCount = 0; 348 | this._lastTextInput = ''; 349 | this._compileTimerDelay = null; 350 | 351 | compiler = compiler || this.state.compiler; 352 | 353 | const description = CompilerDescriptions[compiler]; 354 | 355 | this.setState({ 356 | compiler, 357 | input: description.example.trim(), 358 | }, () => { 359 | if (description.offline) { 360 | if (!description.loaded && description.scripts && description.scripts.length) { 361 | if (description.scripts.length > 1) { 362 | window.$script.order(description.scripts.slice(), () => { 363 | description.loaded = true; 364 | this.onScriptLoad(); 365 | }); 366 | } else { 367 | window.$script(description.scripts[0], () => { 368 | description.loaded = true; 369 | this.onScriptLoad(); 370 | }); 371 | } 372 | 373 | } else { 374 | this.onScriptLoad(); 375 | } 376 | } else { 377 | this.onScriptLoad(); 378 | } 379 | }); 380 | } 381 | 382 | onScriptLoad() { 383 | this.setState({ compilerReady: true }, () => { 384 | getCompilerVersion(this.state.compiler, version => this.setState({ version })); 385 | this.updateCompilation(); 386 | }); 387 | } 388 | 389 | onScriptError = () => { 390 | console.error('Script not load'); 391 | this.setState({ 392 | compilerReady: false, 393 | }); 394 | } 395 | 396 | onSplitPositionChange = size => { 397 | this.handleSize(size); 398 | } 399 | 400 | onCompileButtonClick = mode => { 401 | this._clearCompileTimeout(); 402 | 403 | if (mode === CompileMode.Auto || mode === CompileMode.Manual) 404 | this.updateCompilation(); 405 | } 406 | 407 | onSettingsOptionChange = (key, value) => { 408 | if (!this.state.compilerReady) return; 409 | this.setState({ [key]: value }, key !== 'base64' ? this.updateCompilation : null); 410 | } 411 | 412 | handleSize = throttle(8, size => { 413 | if (this.splitEditor) { 414 | if (!this._cachedClientRect) 415 | this._cachedClientRect = ReactDOM.findDOMNode(this.splitEditor).getBoundingClientRect(); 416 | 417 | const { width, height } = this._cachedClientRect; 418 | const pos = (size ? size / width : this.state.splitPosition); 419 | const primaryWidth = width * pos; 420 | 421 | this.setState({ 422 | inputEditorWidth: Math.ceil(primaryWidth), 423 | outputEditorWidth: Math.ceil(width - primaryWidth - SplitGripWidth), 424 | editorsHeight: height - 160, 425 | splitPosition: pos, 426 | }); 427 | 428 | this.splitEditor.setSize( 429 | { primary: 'first', size: primaryWidth }, 430 | { draggedSize: primaryWidth } 431 | ); 432 | } 433 | }) 434 | 435 | addNotification = (message) => { 436 | // skip notifications for Auto compile mode 437 | // if (this.state.compileMode === CompileMode.Auto) { 438 | // return; 439 | // } 440 | 441 | const { 442 | notifications, 443 | notificationCount, 444 | } = this.state; 445 | 446 | const id = notifications.size + 1; 447 | const newCount = notificationCount + 1; 448 | return this.setState({ 449 | notificationCount: newCount, 450 | notifications: notifications.add({ 451 | id, 452 | message, 453 | key: newCount, 454 | action: '✕', 455 | dismissAfter: 5000, 456 | actionStyle: { 457 | borderRadius: 0, 458 | paddingLeft: '1.5rem', 459 | paddingRight: '0.6rem', 460 | fontSize: '1.8rem', 461 | color: '#fff', 462 | }, 463 | onClick: this.removeAllNotifications, 464 | }) 465 | }); 466 | } 467 | 468 | addAnnotation = (message, type = 'error') => { 469 | const posRegex = /[{([]\s*(\d+)\s*,\s*(\d+)\s*[\])}]$/; 470 | const matches = posRegex.exec(message); 471 | if (matches && matches.length === 3) { 472 | var row = matches[1] >>> 0; 473 | // var column = matches[2] >>> 0; 474 | 475 | this.setState(({ annotations }) => ({ 476 | annotations: annotations.add({ row, type, text: message }), 477 | })); 478 | } 479 | } 480 | 481 | removeAllAnnotation = () => { 482 | this.setState({ annotations: OrderedSet() }); 483 | } 484 | 485 | removeNotification = index => { 486 | const { notifications } = this.state; 487 | return this.setState({ 488 | notifications: notifications.filter(n => n.key !== index), 489 | }) 490 | } 491 | 492 | removeAllNotifications = () => { 493 | return this.setState({ 494 | notificationCount: 0, 495 | notifications: OrderedSet(), 496 | }); 497 | } 498 | 499 | render() { 500 | const { 501 | version, 502 | compiler, 503 | 504 | compilerReady, 505 | compileStatus, 506 | notifications, 507 | annotations, 508 | additionalStatusMessage, 509 | 510 | splitPosition, 511 | inputEditorWidth, 512 | outputEditorWidth, 513 | editorsHeight, 514 | 515 | input, 516 | output, 517 | outputType, 518 | base64, 519 | 520 | inputCursorPosition, 521 | 522 | } = this.state; 523 | 524 | function notificationStyle(index, style, notification) { 525 | return { 526 | zOrder: 999, 527 | color: '#fff', 528 | background: '#f00', 529 | fontSize: '1.5rem', 530 | padding: '1.2em', 531 | paddingLeft: '1.8em', 532 | borderRadius: 0, 533 | left: '5.3em', 534 | bottom: `${4.2 + (index * 3.6)}em` 535 | }; 536 | } 537 | 538 | const errorNotifications = notifications ? ( this.setState({ 542 | notifications: this.state.notifications.delete(notification) 543 | }) } 544 | />) : null; 545 | 546 | const canBinaryDownload = ( 547 | compilerReady && 548 | compileStatus === 'success' && 549 | output.binary 550 | ); 551 | 552 | let busyState = 'pending'; 553 | if (compilerReady) { 554 | switch (compileStatus) { 555 | case 'success': busyState = 'success'; break; 556 | case 'failure': busyState = 'failure'; break; 557 | default: break; 558 | } 559 | } 560 | 561 | return ( 562 |
563 | this.toolbar = self } 565 | version={ version } 566 | compiler={ compiler } 567 | compileDisabled={ !compilerReady } 568 | onCompilerChange={ this.changeCompiler } 569 | onCompileClick={ this.onCompileButtonClick } 570 | onCompileModeChange={ mode => { 571 | this._clearCompileTimeout(); 572 | this.setState({ compileMode: mode }); 573 | if (mode === CompileMode.Auto) 574 | this.updateCompilationWithDelay(AutoCompilationDelay); 575 | }} 576 | onSettingsOptionChange={ this.onSettingsOptionChange } 577 | onOutputSelect={ type => this.setState({ outputType: type }) } 578 | /> 579 | 580 | this.splitEditor = self } 582 | split="vertical" 583 | minSize={ 200 } 584 | defaultSize={ splitPosition * 100 + '%' } 585 | onChange={ this.onSplitPositionChange } 586 | style={{ 587 | margin: '12px', 588 | }} 589 | > 590 | this.inputEditor = self } 594 | width={ inputEditorWidth } 595 | height={ editorsHeight } 596 | code={ input } 597 | annotations={ annotations.toArray() } 598 | onChange={ this.onInputChange } 599 | onPositionChange={ this.onInputPositionChange } 600 | /> 601 | this.outputEditor = self } 606 | width={ outputEditorWidth } 607 | height={ editorsHeight } 608 | code={ formatCode(output[outputType], base64) } 609 | /> 610 | 611 | 612 | 621 | 622 | { errorNotifications } 623 |
624 | ); 625 | } 626 | } 627 | --------------------------------------------------------------------------------