2020-04-27 16:00:54 +00:00
// EspruinoTools bundle (https://github.com/espruino/EspruinoTools)
2020-04-29 08:20:18 +00:00
// Created with https://github.com/espruino/EspruinoWebIDE/blob/gh-pages/extras/create_espruinotools_js.sh
// Based on EspruinoWebIDE 0.73.4
2020-04-27 15:12:13 +00:00
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Initialisation code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
var Espruino ;
( function ( ) {
/ * * L i s t o f p r o c e s s o r s . T h e s e a r e f u n c t i o n s t h a t a r e c a l l e d o n e
* after the other with the data received from the last one .
*
* Common processors are :
*
* sending - sending code to Espruino ( no data )
* transformForEspruino - transform code ready to be sent to Espruino
* transformModuleForEspruino ( { code , name } )
* - transform module code before it 's sent to Espruino with Modules.addCached (we only do this if we don' t think it ' s been minified before )
* connected - connected to Espruino ( no data )
* disconnected - disconnected from Espruino ( no data )
* environmentVar - Board ' s process . env loaded ( object to be saved into Espruino . Env . environmentData )
* boardJSONLoaded - Board ' s JSON was loaded into environmentVar
* getModule - Called with data = { moduleName : "foo" , moduleCode : undefined } - moduleCode should be filled in if the module can be found
* getURL - Called with data = { url : "http://...." , data : undefined ) - data should be filled in if the URL is handled ( See Espruino . Core . Utils . getURL to use this )
* terminalClear - terminal has been cleared
* terminalPrompt - we 've received a ' > ' character (eg, `>` or `debug>`). The argument is the current line' s contents .
* terminalNewLine - When we get a new line on the terminal , this gets called with the last line ' s contents
* debugMode - called with true or false when debug mode is entered or left
* editorHover - called with { node : htmlNode , showTooltip : function ( htmlNode ) } when something is hovered over
* notification - called with { mdg , type : "success" , "error" / "warning" / "info" }
* * /
var processors = { } ;
function init ( ) {
Espruino . Core . Config . loadConfiguration ( function ( ) {
// Initialise all modules
function initModule ( modName , mod ) {
console . log ( "Initialising " + modName ) ;
if ( mod . init !== undefined )
mod . init ( ) ;
}
var module ;
for ( module in Espruino . Core ) initModule ( module , Espruino . Core [ module ] ) ;
for ( module in Espruino . Plugins ) initModule ( module , Espruino . Plugins [ module ] ) ;
callProcessor ( "initialised" , undefined , function ( ) {
// We need the delay because of background.js's url_handler...
setTimeout ( function ( ) {
Espruino . initialised = true ;
} , 1000 ) ;
} ) ;
} ) ;
}
2020-04-29 08:20:18 +00:00
// Automatically start up when all is loaded
document . addEventListener ( "DOMContentLoaded" , init ) ;
2020-04-27 15:12:13 +00:00
/** Add a processor function of type function(data,callback) */
function addProcessor ( eventType , processor ) {
if ( processors [ eventType ] === undefined )
processors [ eventType ] = [ ] ;
processors [ eventType ] . push ( processor ) ;
}
/** Call a processor function */
function callProcessor ( eventType , data , callback ) {
var p = processors [ eventType ] ;
// no processors
if ( p === undefined || p . length == 0 ) {
if ( callback !== undefined ) callback ( data ) ;
return ;
}
// now go through all processors
var n = 0 ;
var cbCalled = false ;
var cb = function ( inData ) {
if ( cbCalled ) throw new Error ( "Internal error in " + eventType + " processor. Callback is called TWICE." ) ;
cbCalled = true ;
if ( n < p . length ) {
cbCalled = false ;
p [ n ++ ] ( inData , cb ) ;
} else {
if ( callback !== undefined ) callback ( inData ) ;
}
} ;
cb ( data ) ;
}
// -----------------------------------
Espruino = {
Core : { } ,
Plugins : { } ,
addProcessor : addProcessor ,
callProcessor : callProcessor ,
initialised : false ,
init : init , // just in case we need to initialise this by hand
} ;
return Espruino ;
} ) ( ) ;
Espruino . Core . Notifications = {
success : function ( e ) { console . log ( e ) ; } ,
error : function ( e ) { console . error ( e ) ; } ,
warning : function ( e ) { console . warn ( e ) ; } ,
2020-04-29 08:20:18 +00:00
info : function ( e ) { console . log ( e ) ; } ,
} ;
2020-04-27 15:12:13 +00:00
Espruino . Core . Status = {
setStatus : function ( e , len ) { console . log ( e ) ; } ,
hasProgress : function ( ) { return false ; } ,
incrementProgress : function ( amt ) { }
} ;
( function ( global , factory ) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory ( exports ) :
typeof define === 'function' && define . amd ? define ( [ 'exports' ] , factory ) :
( factory ( ( global . acorn = { } ) ) ) ;
} ( this , ( function ( exports ) { 'use strict' ;
// Reserved word lists for various dialects of the language
var reservedWords = {
3 : "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile" ,
5 : "class enum extends super const export import" ,
6 : "enum" ,
strict : "implements interface let package private protected public static yield" ,
strictBind : "eval arguments"
} ;
// And the keywords
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this" ;
var keywords = {
5 : ecma5AndLessKeywords ,
6 : ecma5AndLessKeywords + " const class extends export import super"
} ;
var keywordRelationalOperator = /^in(stanceof)?$/ ;
// ## Character categories
// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `bin/generate-identifier-regex.js`.
var nonASCIIidentifierStartChars = " \ xaa \ xb5 \ xba \ xc0 - \ xd6 \ xd8 - \ xf6 \ xf8 - \u02c1\u02c6 - \u02d1\u02e0 - \u02e4\u02ec\u02ee\u0370 - \u0374\u0376\u0377\u037a - \u037d\u037f\u0386\u0388 - \u038a\u038c\u038e - \u03a1\u03a3 - \u03f5\u03f7 - \u0481\u048a - \u052f\u0531 - \u0556\u0559\u0561 - \u0587\u05d0 - \u05ea\u05f0 - \u05f2\u0620 - \u064a\u066e\u066f\u0671 - \u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa - \u06fc\u06ff\u0710\u0712 - \u072f\u074d - \u07a5\u07b1\u07ca - \u07ea\u07f4\u07f5\u07fa\u0800 - \u0815\u081a\u0824\u0828\u0840 - \u0858\u08a0 - \u08b4\u08b6 - \u08bd\u0904 - \u0939\u093d\u0950\u0958 - \u0961\u0971 - \u0980\u0985 - \u098c\u098f\u0990\u0993 - \u09a8\u09aa - \u09b0\u09b2\u09b6 - \u09b9\u09bd\u09ce\u09dc\u09dd\u09df - \u09e1\u09f0\u09f1\u0a05 - \u0a0a\u0a0f\u0a10\u0a13 - \u0a28\u0a2a - \u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59 - \u0a5c\u0a5e\u0a72 - \u0a74\u0a85 - \u0a8d\u0a8f - \u0a91\u0a93 - \u0aa8\u0aaa - \u0ab0\u0ab2\u0ab3\u0ab5 - \u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05 - \u0b0c\u0b0f\u0b10\u0b13 - \u0b28\u0b2a - \u0b30\u0b32\u0b33\u0b35 - \u0b39\u0b3d\u0b5c\u0b5d\u0b5f - \u0b61\u0b71\u0b83\u0b85 - \u0b8a\u0b8e - \u0b90\u0b92 - \u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8 - \u0baa\u0bae - \u0bb9\u0bd0\u0c05 - \u0c0c\u0c0e - \u0c10\u0c12 - \u0c28\u0c2a - \u0c39\u0c3d\u0c58 - \u0c5a\u0c60\u0c61\u0c80\u0c85 - \u0c8c\u0c8e - \u0c90\u0c92 - \u0ca8\u0caa - \u0cb3\u0cb5 - \u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05 - \u0d0c\u0d0e - \u0d10\u0d12 - \u0d3a\u0d3d\u0d4e\u0d54 - \u0d56\u0d5f - \u0d61\u0d7a - \u0d7f\u0d85 - \u0d96\u0d9a - \u0db1\u0db3 - \u0dbb\u0dbd\u0dc0 - \u0dc6\u0e01 - \u0e30\u0e32\u0e33\u0e40 - \u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94 - \u0e97\u0e99 - \u0e9f\u0ea1 - \u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead - \u0eb0\u0eb2\u0eb3\u0ebd\u0ec0 - \u0ec4\u0ec6\u0edc - \u0edf\u0f00\u0f40 - \u0f47\u0f49 - \u0f6c\u0f88 - \u0f8c\u1000 - \u102a\u103f\u1050 - \u1055\u105a - \u105d\u1061\u1065\u1066\u106e - \u1070\u1075 - \u1081\u108e\u10a0 - \u10c5\u10c7\u10cd\u10d0 - \u10fa\u10fc - \u1248\u124a - \u124d\u1250 - \u1256\u1258\u125a - \u125d\u1260 - \u1288\u128a - \u128d\u1290 - \u12b0\u12b2 - \u12b5\u12b8 - \u12be\u12c0\u12c2 - \u12c5\u12c8 - \u12d6\u12d8 - \u1310\u1312 - \u1315\u1318 - \u135a\u1380 - \u138f\u13a0 - \u13f5\u13f8 - \u13fd\u1401 - \u166c\u166f - \u167f\u1681 - \u169a\u16a0 - \u16ea\u16ee - \u16f8\u1700 - \u170c\u170e - \u1711\u1720 - \u1731\u1740 - \u1751\u1760 - \u176c\u176e - \u1770\u1780 - \u17b3\u17d7\u17dc\u1820 - \u1877\u1880 - \u18a8\u18aa\u18b0 - \u18f5\u1900 - \u191e\u1950 - \u196d\u1970 - \u1974\u1980 - \u19ab\u19b0 - \u19c9\u1a00 - \u1a16\u1a20 - \u1a54\u1aa7\u1b05 - \u1b33\u1b45 - \u1b4b\u1b83 - \u1ba0\u1bae\u1baf\u1bba - \u1be5\u1c00 - \u1c23\u1c4d - \u1c4f\u1c5a - \u1c7d\u1c80 - \u1c88\u1ce9 - \u1cec\u1cee - \u1cf1\u1cf5\u1cf6\u1d00 - \u1dbf\u1e00 - \u1f15\u1f18 - \u1f1d\u1f20 - \u1f45\u1f48 - \u1f4d\u1f50 - \u1f57\u1f59\u1f5b\u1f5d\u1f5f - \u1f7d\u1f80 - \u1fb4\u1fb6 - \u1fbc\u1fbe\u1fc2 - \u1fc4\u1fc6 - \u1fcc\u1fd0 - \u1fd3\u1fd6 - \u1fdb\u1fe0 - \u1fec\u1ff2 - \u1ff4\u1ff6 - \u1ffc\u2071\u207f\u2090 - \u209c\u2102\u2107\u210a - \u2113\u2115\u2118 - \u211d\u2124\u2126\u2128\u212a - \u2139\u213c - \u213f\u2145 - \u2149\u214e\u2160 - \u2188\u2c00 - \u2c2e\u2c30 - \u2c5e\u2c60 - \u2ce4\u2ceb - \u2cee\u2cf2\u2cf3\u2d00 - \u2d25\u2d27\u2d2d\u2d30 - \u2d67\u2d6f\u2d80 - \u2d96\u2da0 - \u2da6\u2da8 - \u2dae\u2db0 - \u2db6\u2db8 - \u2dbe\u2dc0 - \u2dc6\u2dc8 - \u2dce\u2dd0 - \u2dd6\u2dd8 - \u2dde\u3005 - \u3007\u3021 - \u3029\u3031 - \u3035\u3038 - \u303c\u3041 - \u3096\u309b - \u309f\u30a1 - \u30fa\u30fc - \u30ff\u3105 - \u312d\u3131 - \u318e\u31a0 - \u31ba\u31f0 - \u31ff\u3400 - \u4db5\u4e00 - \u9fd5\ua000 - \ua48c\ua4d0 - \ua4fd\ua500 - \ua60c\ua610 - \ua61f\ua62a\ua62b\ua640 - \ua66e\ua67f - \ua69d\ua6a0 - \ua6ef\ua717 - \ua71f\ua722 - \ua788\ua78b - \ua7ae\ua7b0 - \ua7b7\ua7f7 - \ua801\ua803 - \ua805\ua807 - \ua80a\ua80c - \ua822\ua840 - \ua873\ua882 - \ua8b3\ua8f2 - \ua8f7\ua8fb\ua8fd\ua90a - \ua925\ua930 - \ua946\ua960 - \ua97c\ua984 - \ua9b2\ua9cf\ua9e0 - \ua9e4\ua9e6 - \ua9ef\ua9fa - \ua9fe\uaa00 - \uaa28\uaa40 - \uaa42\uaa44 - \uaa4b\uaa60 - \uaa76\uaa7a\uaa7e - \uaaaf\uaab1\uaab5\uaab6\uaab9 - \uaabd\uaac0\uaac2\uaadb - \uaadd\uaae0 - \uaaea\uaaf2 - \uaaf4\uab01 - \uab06\uab09 - \uab0e\uab11 - \uab16\uab20 - \uab26\uab28 - \uab2e\uab30 - \uab5a\uab5c - \uab65\uab70 - \uabe2\uac00 - \ud7a3\ud7b0 - \ud7c6\ud7cb - \ud7fb\uf900 - \ufa6d\ufa70 - \ufad9\ufb00 - \ufb06\ufb13 - \ufb17\ufb1d\ufb1f - \ufb28\ufb2a - \ufb36\ufb38 - \ u
var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f" ;
var nonASCIIidentifierStart = new RegExp ( "[" + nonASCIIidentifierStartChars + "]" ) ;
var nonASCIIidentifier = new RegExp ( "[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]" ) ;
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null ;
// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by bin/generate-identifier-regex.js
// eslint-disable-next-line comma-spacing
var astralIdentifierStartCodes = [ 0 , 11 , 2 , 25 , 2 , 18 , 2 , 1 , 2 , 14 , 3 , 13 , 35 , 122 , 70 , 52 , 268 , 28 , 4 , 48 , 48 , 31 , 17 , 26 , 6 , 37 , 11 , 29 , 3 , 35 , 5 , 7 , 2 , 4 , 43 , 157 , 19 , 35 , 5 , 35 , 5 , 39 , 9 , 51 , 157 , 310 , 10 , 21 , 11 , 7 , 153 , 5 , 3 , 0 , 2 , 43 , 2 , 1 , 4 , 0 , 3 , 22 , 11 , 22 , 10 , 30 , 66 , 18 , 2 , 1 , 11 , 21 , 11 , 25 , 71 , 55 , 7 , 1 , 65 , 0 , 16 , 3 , 2 , 2 , 2 , 26 , 45 , 28 , 4 , 28 , 36 , 7 , 2 , 27 , 28 , 53 , 11 , 21 , 11 , 18 , 14 , 17 , 111 , 72 , 56 , 50 , 14 , 50 , 785 , 52 , 76 , 44 , 33 , 24 , 27 , 35 , 42 , 34 , 4 , 0 , 13 , 47 , 15 , 3 , 22 , 0 , 2 , 0 , 36 , 17 , 2 , 24 , 85 , 6 , 2 , 0 , 2 , 3 , 2 , 14 , 2 , 9 , 8 , 46 , 39 , 7 , 3 , 1 , 3 , 21 , 2 , 6 , 2 , 1 , 2 , 4 , 4 , 0 , 19 , 0 , 13 , 4 , 159 , 52 , 19 , 3 , 54 , 47 , 21 , 1 , 2 , 0 , 185 , 46 , 42 , 3 , 37 , 47 , 21 , 0 , 60 , 42 , 86 , 25 , 391 , 63 , 32 , 0 , 449 , 56 , 264 , 8 , 2 , 36 , 18 , 0 , 50 , 29 , 881 , 921 , 103 , 110 , 18 , 195 , 2749 , 1070 , 4050 , 582 , 8634 , 568 , 8 , 30 , 114 , 29 , 19 , 47 , 17 , 3 , 32 , 20 , 6 , 18 , 881 , 68 , 12 , 0 , 67 , 12 , 65 , 0 , 32 , 6124 , 20 , 754 , 9486 , 1 , 3071 , 106 , 6 , 12 , 4 , 8 , 8 , 9 , 5991 , 84 , 2 , 70 , 2 , 1 , 3 , 0 , 3 , 1 , 3 , 3 , 2 , 11 , 2 , 0 , 2 , 6 , 2 , 64 , 2 , 3 , 3 , 7 , 2 , 6 , 2 , 27 , 2 , 3 , 2 , 4 , 2 , 0 , 4 , 6 , 2 , 339 , 3 , 24 , 2 , 24 , 2 , 30 , 2 , 24 , 2 , 30 , 2 , 24 , 2 , 30 , 2 , 24 , 2 , 30 , 2 , 24 , 2 , 7 , 4149 , 196 , 60 , 67 , 1213 , 3 , 2 , 26 , 2 , 1 , 2 , 0 , 3 , 0 , 2 , 9 , 2 , 3 , 2 , 0 , 2 , 0 , 7 , 0 , 5 , 0 , 2 , 0 , 2 , 0 , 2 , 2 , 2 , 1 , 2 , 0 , 3 , 0 , 2 , 0 , 2 , 0 , 2 , 0 , 2 , 0 , 2 , 1 , 2 , 0 , 3 , 3 , 2 , 6 , 2 , 3 , 2 , 3 , 2 , 0 , 2 , 9 , 2 , 16 , 6 , 2 , 2 , 4 , 2 , 16 , 4421 , 42710 , 42 , 4148 , 12 , 221 , 3 , 5761 , 10591 , 541 ] ;
// eslint-disable-next-line comma-spacing
var astralIdentifierCodes = [ 509 , 0 , 227 , 0 , 150 , 4 , 294 , 9 , 1368 , 2 , 2 , 1 , 6 , 3 , 41 , 2 , 5 , 0 , 166 , 1 , 1306 , 2 , 54 , 14 , 32 , 9 , 16 , 3 , 46 , 10 , 54 , 9 , 7 , 2 , 37 , 13 , 2 , 9 , 52 , 0 , 13 , 2 , 49 , 13 , 10 , 2 , 4 , 9 , 83 , 11 , 7 , 0 , 161 , 11 , 6 , 9 , 7 , 3 , 57 , 0 , 2 , 6 , 3 , 1 , 3 , 2 , 10 , 0 , 11 , 1 , 3 , 6 , 4 , 4 , 193 , 17 , 10 , 9 , 87 , 19 , 13 , 9 , 214 , 6 , 3 , 8 , 28 , 1 , 83 , 16 , 16 , 9 , 82 , 12 , 9 , 9 , 84 , 14 , 5 , 9 , 423 , 9 , 838 , 7 , 2 , 7 , 17 , 9 , 57 , 21 , 2 , 13 , 19882 , 9 , 135 , 4 , 60 , 6 , 26 , 9 , 1016 , 45 , 17 , 3 , 19723 , 1 , 5319 , 4 , 4 , 5 , 9 , 7 , 3 , 6 , 31 , 3 , 149 , 2 , 1418 , 49 , 513 , 54 , 5 , 49 , 9 , 0 , 15 , 0 , 23 , 4 , 2 , 14 , 1361 , 6 , 2 , 16 , 3 , 6 , 2 , 1 , 2 , 4 , 2214 , 6 , 110 , 6 , 6 , 9 , 792487 , 239 ] ;
// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet ( code , set ) {
var pos = 0x10000 ;
for ( var i = 0 ; i < set . length ; i += 2 ) {
pos += set [ i ] ;
if ( pos > code ) { return false }
pos += set [ i + 1 ] ;
if ( pos >= code ) { return true }
}
}
// Test whether a given character code starts an identifier.
function isIdentifierStart ( code , astral ) {
if ( code < 65 ) { return code === 36 }
if ( code < 91 ) { return true }
if ( code < 97 ) { return code === 95 }
if ( code < 123 ) { return true }
if ( code <= 0xffff ) { return code >= 0xaa && nonASCIIidentifierStart . test ( String . fromCharCode ( code ) ) }
if ( astral === false ) { return false }
return isInAstralSet ( code , astralIdentifierStartCodes )
}
// Test whether a given character is part of an identifier.
function isIdentifierChar ( code , astral ) {
if ( code < 48 ) { return code === 36 }
if ( code < 58 ) { return true }
if ( code < 65 ) { return false }
if ( code < 91 ) { return true }
if ( code < 97 ) { return code === 95 }
if ( code < 123 ) { return true }
if ( code <= 0xffff ) { return code >= 0xaa && nonASCIIidentifier . test ( String . fromCharCode ( code ) ) }
if ( astral === false ) { return false }
return isInAstralSet ( code , astralIdentifierStartCodes ) || isInAstralSet ( code , astralIdentifierCodes )
}
// ## Token types
// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.
// All token type variables start with an underscore, to make them
// easy to recognize.
// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// The `startsExpr` property is used to check if the token ends a
// `yield` expression. It is set on all token types that either can
// directly start an expression (like a quotation mark) or can
// continue an expression (like the body of a string).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.
var TokenType = function TokenType ( label , conf ) {
if ( conf === void 0 ) conf = { } ;
this . label = label ;
this . keyword = conf . keyword ;
this . beforeExpr = ! ! conf . beforeExpr ;
this . startsExpr = ! ! conf . startsExpr ;
this . isLoop = ! ! conf . isLoop ;
this . isAssign = ! ! conf . isAssign ;
this . prefix = ! ! conf . prefix ;
this . postfix = ! ! conf . postfix ;
this . binop = conf . binop || null ;
this . updateContext = null ;
} ;
function binop ( name , prec ) {
return new TokenType ( name , { beforeExpr : true , binop : prec } )
}
var beforeExpr = { beforeExpr : true } ;
var startsExpr = { startsExpr : true } ;
// Map keyword names to token types.
var keywords$1 = { } ;
// Succinct definitions of keyword token types
function kw ( name , options ) {
if ( options === void 0 ) options = { } ;
options . keyword = name ;
return keywords$1 [ name ] = new TokenType ( name , options )
}
var types = {
num : new TokenType ( "num" , startsExpr ) ,
regexp : new TokenType ( "regexp" , startsExpr ) ,
string : new TokenType ( "string" , startsExpr ) ,
name : new TokenType ( "name" , startsExpr ) ,
eof : new TokenType ( "eof" ) ,
// Punctuation token types.
bracketL : new TokenType ( "[" , { beforeExpr : true , startsExpr : true } ) ,
bracketR : new TokenType ( "]" ) ,
braceL : new TokenType ( "{" , { beforeExpr : true , startsExpr : true } ) ,
braceR : new TokenType ( "}" ) ,
parenL : new TokenType ( "(" , { beforeExpr : true , startsExpr : true } ) ,
parenR : new TokenType ( ")" ) ,
comma : new TokenType ( "," , beforeExpr ) ,
semi : new TokenType ( ";" , beforeExpr ) ,
colon : new TokenType ( ":" , beforeExpr ) ,
dot : new TokenType ( "." ) ,
question : new TokenType ( "?" , beforeExpr ) ,
arrow : new TokenType ( "=>" , beforeExpr ) ,
template : new TokenType ( "template" ) ,
invalidTemplate : new TokenType ( "invalidTemplate" ) ,
ellipsis : new TokenType ( "..." , beforeExpr ) ,
backQuote : new TokenType ( "`" , startsExpr ) ,
dollarBraceL : new TokenType ( "${" , { beforeExpr : true , startsExpr : true } ) ,
// Operators. These carry several kinds of properties to help the
// parser use them properly (the presence of these properties is
// what categorizes them as operators).
//
// `binop`, when present, specifies that this operator is a binary
// operator, and will refer to its precedence.
//
// `prefix` and `postfix` mark the operator as a prefix or postfix
// unary operator.
//
// `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
// binary operators with a very low precedence, that should result
// in AssignmentExpression nodes.
eq : new TokenType ( "=" , { beforeExpr : true , isAssign : true } ) ,
assign : new TokenType ( "_=" , { beforeExpr : true , isAssign : true } ) ,
incDec : new TokenType ( "++/--" , { prefix : true , postfix : true , startsExpr : true } ) ,
prefix : new TokenType ( "!/~" , { beforeExpr : true , prefix : true , startsExpr : true } ) ,
logicalOR : binop ( "||" , 1 ) ,
logicalAND : binop ( "&&" , 2 ) ,
bitwiseOR : binop ( "|" , 3 ) ,
bitwiseXOR : binop ( "^" , 4 ) ,
bitwiseAND : binop ( "&" , 5 ) ,
equality : binop ( "==/!=/===/!==" , 6 ) ,
relational : binop ( "</>/<=/>=" , 7 ) ,
bitShift : binop ( "<</>>/>>>" , 8 ) ,
plusMin : new TokenType ( "+/-" , { beforeExpr : true , binop : 9 , prefix : true , startsExpr : true } ) ,
modulo : binop ( "%" , 10 ) ,
star : binop ( "*" , 10 ) ,
slash : binop ( "/" , 10 ) ,
starstar : new TokenType ( "**" , { beforeExpr : true } ) ,
// Keyword token types.
_break : kw ( "break" ) ,
_case : kw ( "case" , beforeExpr ) ,
_catch : kw ( "catch" ) ,
_continue : kw ( "continue" ) ,
_debugger : kw ( "debugger" ) ,
_default : kw ( "default" , beforeExpr ) ,
_do : kw ( "do" , { isLoop : true , beforeExpr : true } ) ,
_else : kw ( "else" , beforeExpr ) ,
_finally : kw ( "finally" ) ,
_for : kw ( "for" , { isLoop : true } ) ,
_function : kw ( "function" , startsExpr ) ,
_if : kw ( "if" ) ,
_return : kw ( "return" , beforeExpr ) ,
_switch : kw ( "switch" ) ,
_throw : kw ( "throw" , beforeExpr ) ,
_try : kw ( "try" ) ,
_var : kw ( "var" ) ,
_const : kw ( "const" ) ,
_while : kw ( "while" , { isLoop : true } ) ,
_with : kw ( "with" ) ,
_new : kw ( "new" , { beforeExpr : true , startsExpr : true } ) ,
_this : kw ( "this" , startsExpr ) ,
_super : kw ( "super" , startsExpr ) ,
_class : kw ( "class" , startsExpr ) ,
_extends : kw ( "extends" , beforeExpr ) ,
_export : kw ( "export" ) ,
_import : kw ( "import" ) ,
_null : kw ( "null" , startsExpr ) ,
_true : kw ( "true" , startsExpr ) ,
_false : kw ( "false" , startsExpr ) ,
_in : kw ( "in" , { beforeExpr : true , binop : 7 } ) ,
_instanceof : kw ( "instanceof" , { beforeExpr : true , binop : 7 } ) ,
_typeof : kw ( "typeof" , { beforeExpr : true , prefix : true , startsExpr : true } ) ,
_void : kw ( "void" , { beforeExpr : true , prefix : true , startsExpr : true } ) ,
_delete : kw ( "delete" , { beforeExpr : true , prefix : true , startsExpr : true } )
} ;
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
var lineBreak = /\r\n?|\n|\u2028|\u2029/ ;
var lineBreakG = new RegExp ( lineBreak . source , "g" ) ;
function isNewLine ( code ) {
return code === 10 || code === 13 || code === 0x2028 || code === 0x2029
}
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/ ;
var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g ;
var ref = Object . prototype ;
var hasOwnProperty = ref . hasOwnProperty ;
var toString = ref . toString ;
// Checks if an object has a property.
function has ( obj , propName ) {
return hasOwnProperty . call ( obj , propName )
}
var isArray = Array . isArray || ( function ( obj ) { return (
toString . call ( obj ) === "[object Array]"
) ; } ) ;
// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.
var Position = function Position ( line , col ) {
this . line = line ;
this . column = col ;
} ;
Position . prototype . offset = function offset ( n ) {
return new Position ( this . line , this . column + n )
} ;
var SourceLocation = function SourceLocation ( p , start , end ) {
this . start = start ;
this . end = end ;
if ( p . sourceFile !== null ) { this . source = p . sourceFile ; }
} ;
// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.
function getLineInfo ( input , offset ) {
for ( var line = 1 , cur = 0 ; ; ) {
lineBreakG . lastIndex = cur ;
var match = lineBreakG . exec ( input ) ;
if ( match && match . index < offset ) {
++ line ;
cur = match . index + match [ 0 ] . length ;
} else {
return new Position ( line , offset - cur )
}
}
}
// A second optional argument can be given to further configure
// the parser process. These options are recognized:
var defaultOptions = {
// `ecmaVersion` indicates the ECMAScript version to parse. Must
// be either 3, 5, 6 (2015), 7 (2016), or 8 (2017). This influences support
// for strict mode, the set of reserved words, and support for
// new syntax features. The default is 7.
ecmaVersion : 7 ,
// `sourceType` indicates the mode the code should be parsed in.
// Can be either `"script"` or `"module"`. This influences global
// strict mode and parsing of `import` and `export` declarations.
sourceType : "script" ,
// `onInsertedSemicolon` can be a callback that will be called
// when a semicolon is automatically inserted. It will be passed
// th position of the comma as an offset, and if `locations` is
// enabled, it is given the location as a `{line, column}` object
// as second argument.
onInsertedSemicolon : null ,
// `onTrailingComma` is similar to `onInsertedSemicolon`, but for
// trailing commas.
onTrailingComma : null ,
// By default, reserved words are only enforced if ecmaVersion >= 5.
// Set `allowReserved` to a boolean value to explicitly turn this on
// an off. When this option has the value "never", reserved words
// and keywords can also not be used as property names.
allowReserved : null ,
// When enabled, a return at the top level is not considered an
// error.
allowReturnOutsideFunction : false ,
// When enabled, import/export statements are not constrained to
// appearing at the top of the program.
allowImportExportEverywhere : false ,
// When enabled, hashbang directive in the beginning of file
// is allowed and treated as a line comment.
allowHashBang : false ,
// When `locations` is on, `loc` properties holding objects with
// `start` and `end` properties in `{line, column}` form (with
// line being 1-based and column 0-based) will be attached to the
// nodes.
locations : false ,
// A function can be passed as `onToken` option, which will
// cause Acorn to call that function with object in the same
// format as tokens returned from `tokenizer().getToken()`. Note
// that you are not allowed to call the parser from the
// callback—that will corrupt its internal state.
onToken : null ,
// A function can be passed as `onComment` option, which will
// cause Acorn to call that function with `(block, text, start,
// end)` parameters whenever a comment is skipped. `block` is a
// boolean indicating whether this is a block (`/* */`) comment,
// `text` is the content of the comment, and `start` and `end` are
// character offsets that denote the start and end of the comment.
// When the `locations` option is on, two more parameters are
// passed, the full `{line, column}` locations of the start and
// end of the comments. Note that you are not allowed to call the
// parser from the callback—that will corrupt its internal state.
onComment : null ,
// Nodes have their start and end characters offsets recorded in
// `start` and `end` properties (directly on the node, rather than
// the `loc` object, which holds line/column data. To also add a
// [semi-standardized][range] `range` property holding a `[start,
// end]` array with the same numbers, set the `ranges` option to
// `true`.
//
// [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
ranges : false ,
// It is possible to parse multiple files into a single AST by
// passing the tree produced by parsing the first file as
// `program` option in subsequent parses. This will add the
// toplevel forms of the parsed file to the `Program` (top) node
// of an existing parse tree.
program : null ,
// When `locations` is on, you can pass this to record the source
// file in every node's `loc` object.
sourceFile : null ,
// This value, if given, is stored in every node, whether
// `locations` is on or off.
directSourceFile : null ,
// When enabled, parenthesized expressions are represented by
// (non-standard) ParenthesizedExpression nodes
preserveParens : false ,
plugins : { }
} ;
// Interpret and default an options object
function getOptions ( opts ) {
var options = { } ;
for ( var opt in defaultOptions )
{ options [ opt ] = opts && has ( opts , opt ) ? opts [ opt ] : defaultOptions [ opt ] ; }
if ( options . ecmaVersion >= 2015 )
{ options . ecmaVersion -= 2009 ; }
if ( options . allowReserved == null )
{ options . allowReserved = options . ecmaVersion < 5 ; }
if ( isArray ( options . onToken ) ) {
var tokens = options . onToken ;
options . onToken = function ( token ) { return tokens . push ( token ) ; } ;
}
if ( isArray ( options . onComment ) )
{ options . onComment = pushComment ( options , options . onComment ) ; }
return options
}
function pushComment ( options , array ) {
return function ( block , text , start , end , startLoc , endLoc ) {
var comment = {
type : block ? "Block" : "Line" ,
value : text ,
start : start ,
end : end
} ;
if ( options . locations )
{ comment . loc = new SourceLocation ( this , startLoc , endLoc ) ; }
if ( options . ranges )
{ comment . range = [ start , end ] ; }
array . push ( comment ) ;
}
}
// Registered plugins
var plugins = { } ;
function keywordRegexp ( words ) {
return new RegExp ( "^(?:" + words . replace ( / /g , "|" ) + ")$" )
}
var Parser = function Parser ( options , input , startPos ) {
this . options = options = getOptions ( options ) ;
this . sourceFile = options . sourceFile ;
this . keywords = keywordRegexp ( keywords [ options . ecmaVersion >= 6 ? 6 : 5 ] ) ;
var reserved = "" ;
if ( ! options . allowReserved ) {
for ( var v = options . ecmaVersion ; ; v -- )
{ if ( reserved = reservedWords [ v ] ) { break } }
if ( options . sourceType == "module" ) { reserved += " await" ; }
}
this . reservedWords = keywordRegexp ( reserved ) ;
var reservedStrict = ( reserved ? reserved + " " : "" ) + reservedWords . strict ;
this . reservedWordsStrict = keywordRegexp ( reservedStrict ) ;
this . reservedWordsStrictBind = keywordRegexp ( reservedStrict + " " + reservedWords . strictBind ) ;
this . input = String ( input ) ;
// Used to signal to callers of `readWord1` whether the word
// contained any escape sequences. This is needed because words with
// escape sequences must not be interpreted as keywords.
this . containsEsc = false ;
// Load plugins
this . loadPlugins ( options . plugins ) ;
// Set up token state
// The current position of the tokenizer in the input.
if ( startPos ) {
this . pos = startPos ;
this . lineStart = this . input . lastIndexOf ( "\n" , startPos - 1 ) + 1 ;
this . curLine = this . input . slice ( 0 , this . lineStart ) . split ( lineBreak ) . length ;
} else {
this . pos = this . lineStart = 0 ;
this . curLine = 1 ;
}
// Properties of the current token:
// Its type
this . type = types . eof ;
// For tokens that include more information than their type, the value
this . value = null ;
// Its start and end offset
this . start = this . end = this . pos ;
// And, if locations are used, the {line, column} object
// corresponding to those offsets
this . startLoc = this . endLoc = this . curPosition ( ) ;
// Position information for the previous token
this . lastTokEndLoc = this . lastTokStartLoc = null ;
this . lastTokStart = this . lastTokEnd = this . pos ;
// The context stack is used to superficially track syntactic
// context to predict whether a regular expression is allowed in a
// given position.
this . context = this . initialContext ( ) ;
this . exprAllowed = true ;
// Figure out if it's a module code.
this . inModule = options . sourceType === "module" ;
this . strict = this . inModule || this . strictDirective ( this . pos ) ;
// Used to signify the start of a potential arrow function
this . potentialArrowAt = - 1 ;
// Flags to track whether we are in a function, a generator, an async function.
this . inFunction = this . inGenerator = this . inAsync = false ;
// Positions to delayed-check that yield/await does not exist in default parameters.
this . yieldPos = this . awaitPos = 0 ;
// Labels in scope.
this . labels = [ ] ;
// If enabled, skip leading hashbang line.
if ( this . pos === 0 && options . allowHashBang && this . input . slice ( 0 , 2 ) === "#!" )
{ this . skipLineComment ( 2 ) ; }
// Scope tracking for duplicate variable names (see scope.js)
this . scopeStack = [ ] ;
this . enterFunctionScope ( ) ;
} ;
// DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them
Parser . prototype . isKeyword = function isKeyword ( word ) { return this . keywords . test ( word ) } ;
Parser . prototype . isReservedWord = function isReservedWord ( word ) { return this . reservedWords . test ( word ) } ;
Parser . prototype . extend = function extend ( name , f ) {
this [ name ] = f ( this [ name ] ) ;
} ;
Parser . prototype . loadPlugins = function loadPlugins ( pluginConfigs ) {
var this $1 = this ;
for ( var name in pluginConfigs ) {
var plugin = plugins [ name ] ;
if ( ! plugin ) { throw new Error ( "Plugin '" + name + "' not found" ) }
plugin ( this $1 , pluginConfigs [ name ] ) ;
}
} ;
Parser . prototype . parse = function parse ( ) {
var node = this . options . program || this . startNode ( ) ;
this . nextToken ( ) ;
return this . parseTopLevel ( node )
} ;
var pp = Parser . prototype ;
// ## Parser utilities
var literal = /^(?:'((?:\\.|[^'])*?)'|"((?:\\.|[^"])*?)"|;)/ ;
pp . strictDirective = function ( start ) {
var this $1 = this ;
for ( ; ; ) {
skipWhiteSpace . lastIndex = start ;
start += skipWhiteSpace . exec ( this $1 . input ) [ 0 ] . length ;
var match = literal . exec ( this $1 . input . slice ( start ) ) ;
if ( ! match ) { return false }
if ( ( match [ 1 ] || match [ 2 ] ) == "use strict" ) { return true }
start += match [ 0 ] . length ;
}
} ;
// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.
pp . eat = function ( type ) {
if ( this . type === type ) {
this . next ( ) ;
return true
} else {
return false
}
} ;
// Tests whether parsed token is a contextual keyword.
pp . isContextual = function ( name ) {
return this . type === types . name && this . value === name && ! this . containsEsc
} ;
// Consumes contextual keyword if possible.
pp . eatContextual = function ( name ) {
if ( ! this . isContextual ( name ) ) { return false }
this . next ( ) ;
return true
} ;
// Asserts that following token is given contextual keyword.
pp . expectContextual = function ( name ) {
if ( ! this . eatContextual ( name ) ) { this . unexpected ( ) ; }
} ;
// Test whether a semicolon can be inserted at the current position.
pp . canInsertSemicolon = function ( ) {
return this . type === types . eof ||
this . type === types . braceR ||
lineBreak . test ( this . input . slice ( this . lastTokEnd , this . start ) )
} ;
pp . insertSemicolon = function ( ) {
if ( this . canInsertSemicolon ( ) ) {
if ( this . options . onInsertedSemicolon )
{ this . options . onInsertedSemicolon ( this . lastTokEnd , this . lastTokEndLoc ) ; }
return true
}
} ;
// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.
pp . semicolon = function ( ) {
if ( ! this . eat ( types . semi ) && ! this . insertSemicolon ( ) ) { this . unexpected ( ) ; }
} ;
pp . afterTrailingComma = function ( tokType , notNext ) {
if ( this . type == tokType ) {
if ( this . options . onTrailingComma )
{ this . options . onTrailingComma ( this . lastTokStart , this . lastTokStartLoc ) ; }
if ( ! notNext )
{ this . next ( ) ; }
return true
}
} ;
// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.
pp . expect = function ( type ) {
this . eat ( type ) || this . unexpected ( ) ;
} ;
// Raise an unexpected token error.
pp . unexpected = function ( pos ) {
this . raise ( pos != null ? pos : this . start , "Unexpected token" ) ;
} ;
function DestructuringErrors ( ) {
this . shorthandAssign =
this . trailingComma =
this . parenthesizedAssign =
this . parenthesizedBind =
this . doubleProto =
- 1 ;
}
pp . checkPatternErrors = function ( refDestructuringErrors , isAssign ) {
if ( ! refDestructuringErrors ) { return }
if ( refDestructuringErrors . trailingComma > - 1 )
{ this . raiseRecoverable ( refDestructuringErrors . trailingComma , "Comma is not permitted after the rest element" ) ; }
var parens = isAssign ? refDestructuringErrors . parenthesizedAssign : refDestructuringErrors . parenthesizedBind ;
if ( parens > - 1 ) { this . raiseRecoverable ( parens , "Parenthesized pattern" ) ; }
} ;
pp . checkExpressionErrors = function ( refDestructuringErrors , andThrow ) {
if ( ! refDestructuringErrors ) { return false }
var shorthandAssign = refDestructuringErrors . shorthandAssign ;
var doubleProto = refDestructuringErrors . doubleProto ;
if ( ! andThrow ) { return shorthandAssign >= 0 || doubleProto >= 0 }
if ( shorthandAssign >= 0 )
{ this . raise ( shorthandAssign , "Shorthand property assignments are valid only in destructuring patterns" ) ; }
if ( doubleProto >= 0 )
{ this . raiseRecoverable ( doubleProto , "Redefinition of __proto__ property" ) ; }
} ;
pp . checkYieldAwaitInDefaultParams = function ( ) {
if ( this . yieldPos && ( ! this . awaitPos || this . yieldPos < this . awaitPos ) )
{ this . raise ( this . yieldPos , "Yield expression cannot be a default value" ) ; }
if ( this . awaitPos )
{ this . raise ( this . awaitPos , "Await expression cannot be a default value" ) ; }
} ;
pp . isSimpleAssignTarget = function ( expr ) {
if ( expr . type === "ParenthesizedExpression" )
{ return this . isSimpleAssignTarget ( expr . expression ) }
return expr . type === "Identifier" || expr . type === "MemberExpression"
} ;
var pp$1 = Parser . prototype ;
// ### Statement parsing
// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node. Optionally takes a
// `program` argument. If present, the statements will be appended
// to its body instead of creating a new node.
pp$1 . parseTopLevel = function ( node ) {
var this $1 = this ;
var exports = { } ;
if ( ! node . body ) { node . body = [ ] ; }
while ( this . type !== types . eof ) {
var stmt = this $1 . parseStatement ( true , true , exports ) ;
node . body . push ( stmt ) ;
}
this . adaptDirectivePrologue ( node . body ) ;
this . next ( ) ;
if ( this . options . ecmaVersion >= 6 ) {
node . sourceType = this . options . sourceType ;
}
return this . finishNode ( node , "Program" )
} ;
var loopLabel = { kind : "loop" } ;
var switchLabel = { kind : "switch" } ;
pp$1 . isLet = function ( ) {
if ( this . options . ecmaVersion < 6 || ! this . isContextual ( "let" ) ) { return false }
skipWhiteSpace . lastIndex = this . pos ;
var skip = skipWhiteSpace . exec ( this . input ) ;
var next = this . pos + skip [ 0 ] . length , nextCh = this . input . charCodeAt ( next ) ;
if ( nextCh === 91 || nextCh == 123 ) { return true } // '{' and '['
if ( isIdentifierStart ( nextCh , true ) ) {
var pos = next + 1 ;
while ( isIdentifierChar ( this . input . charCodeAt ( pos ) , true ) ) { ++ pos ; }
var ident = this . input . slice ( next , pos ) ;
if ( ! keywordRelationalOperator . test ( ident ) ) { return true }
}
return false
} ;
// check 'async [no LineTerminator here] function'
// - 'async /*foo*/ function' is OK.
// - 'async /*\n*/ function' is invalid.
pp$1 . isAsyncFunction = function ( ) {
if ( this . options . ecmaVersion < 8 || ! this . isContextual ( "async" ) )
{ return false }
skipWhiteSpace . lastIndex = this . pos ;
var skip = skipWhiteSpace . exec ( this . input ) ;
var next = this . pos + skip [ 0 ] . length ;
return ! lineBreak . test ( this . input . slice ( this . pos , next ) ) &&
this . input . slice ( next , next + 8 ) === "function" &&
( next + 8 == this . input . length || ! isIdentifierChar ( this . input . charAt ( next + 8 ) ) )
} ;
// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.
pp$1 . parseStatement = function ( declaration , topLevel , exports ) {
var starttype = this . type , node = this . startNode ( ) , kind ;
if ( this . isLet ( ) ) {
starttype = types . _var ;
kind = "let" ;
}
// Most types of statements are recognized by the keyword they
// start with. Many are trivial to parse, some require a bit of
// complexity.
switch ( starttype ) {
case types . _break : case types . _continue : return this . parseBreakContinueStatement ( node , starttype . keyword )
case types . _debugger : return this . parseDebuggerStatement ( node )
case types . _do : return this . parseDoStatement ( node )
case types . _for : return this . parseForStatement ( node )
case types . _function :
if ( ! declaration && this . options . ecmaVersion >= 6 ) { this . unexpected ( ) ; }
return this . parseFunctionStatement ( node , false )
case types . _class :
if ( ! declaration ) { this . unexpected ( ) ; }
return this . parseClass ( node , true )
case types . _if : return this . parseIfStatement ( node )
case types . _return : return this . parseReturnStatement ( node )
case types . _switch : return this . parseSwitchStatement ( node )
case types . _throw : return this . parseThrowStatement ( node )
case types . _try : return this . parseTryStatement ( node )
case types . _const : case types . _var :
kind = kind || this . value ;
if ( ! declaration && kind != "var" ) { this . unexpected ( ) ; }
return this . parseVarStatement ( node , kind )
case types . _while : return this . parseWhileStatement ( node )
case types . _with : return this . parseWithStatement ( node )
case types . braceL : return this . parseBlock ( )
case types . semi : return this . parseEmptyStatement ( node )
case types . _export :
case types . _import :
if ( ! this . options . allowImportExportEverywhere ) {
if ( ! topLevel )
{ this . raise ( this . start , "'import' and 'export' may only appear at the top level" ) ; }
if ( ! this . inModule )
{ this . raise ( this . start , "'import' and 'export' may appear only with 'sourceType: module'" ) ; }
}
return starttype === types . _import ? this . parseImport ( node ) : this . parseExport ( node , exports )
// If the statement does not start with a statement keyword or a
// brace, it's an ExpressionStatement or LabeledStatement. We
// simply start parsing an expression, and afterwards, if the
// next token is a colon and the expression was a simple
// Identifier node, we switch to interpreting it as a label.
default :
if ( this . isAsyncFunction ( ) ) {
if ( ! declaration ) { this . unexpected ( ) ; }
this . next ( ) ;
return this . parseFunctionStatement ( node , true )
}
var maybeName = this . value , expr = this . parseExpression ( ) ;
if ( starttype === types . name && expr . type === "Identifier" && this . eat ( types . colon ) )
{ return this . parseLabeledStatement ( node , maybeName , expr ) }
else { return this . parseExpressionStatement ( node , expr ) }
}
} ;
pp$1 . parseBreakContinueStatement = function ( node , keyword ) {
var this $1 = this ;
var isBreak = keyword == "break" ;
this . next ( ) ;
if ( this . eat ( types . semi ) || this . insertSemicolon ( ) ) { node . label = null ; }
else if ( this . type !== types . name ) { this . unexpected ( ) ; }
else {
node . label = this . parseIdent ( ) ;
this . semicolon ( ) ;
}
// Verify that there is an actual destination to break or
// continue to.
var i = 0 ;
for ( ; i < this . labels . length ; ++ i ) {
var lab = this $1 . labels [ i ] ;
if ( node . label == null || lab . name === node . label . name ) {
if ( lab . kind != null && ( isBreak || lab . kind === "loop" ) ) { break }
if ( node . label && isBreak ) { break }
}
}
if ( i === this . labels . length ) { this . raise ( node . start , "Unsyntactic " + keyword ) ; }
return this . finishNode ( node , isBreak ? "BreakStatement" : "ContinueStatement" )
} ;
pp$1 . parseDebuggerStatement = function ( node ) {
this . next ( ) ;
this . semicolon ( ) ;
return this . finishNode ( node , "DebuggerStatement" )
} ;
pp$1 . parseDoStatement = function ( node ) {
this . next ( ) ;
this . labels . push ( loopLabel ) ;
node . body = this . parseStatement ( false ) ;
this . labels . pop ( ) ;
this . expect ( types . _while ) ;
node . test = this . parseParenExpression ( ) ;
if ( this . options . ecmaVersion >= 6 )
{ this . eat ( types . semi ) ; }
else
{ this . semicolon ( ) ; }
return this . finishNode ( node , "DoWhileStatement" )
} ;
// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
// statement or expression, disallowing the `in` operator (see
// the second parameter to `parseExpression`), and then check
// whether the next token is `in` or `of`. When there is no init
// part (semicolon immediately after the opening parenthesis), it
// is a regular `for` loop.
pp$1 . parseForStatement = function ( node ) {
this . next ( ) ;
var awaitAt = ( this . options . ecmaVersion >= 9 && this . inAsync && this . eatContextual ( "await" ) ) ? this . lastTokStart : - 1 ;
this . labels . push ( loopLabel ) ;
this . enterLexicalScope ( ) ;
this . expect ( types . parenL ) ;
if ( this . type === types . semi ) {
if ( awaitAt > - 1 ) { this . unexpected ( awaitAt ) ; }
return this . parseFor ( node , null )
}
var isLet = this . isLet ( ) ;
if ( this . type === types . _var || this . type === types . _const || isLet ) {
var init$1 = this . startNode ( ) , kind = isLet ? "let" : this . value ;
this . next ( ) ;
this . parseVar ( init$1 , true , kind ) ;
this . finishNode ( init$1 , "VariableDeclaration" ) ;
if ( ( this . type === types . _in || ( this . options . ecmaVersion >= 6 && this . isContextual ( "of" ) ) ) && init$1 . declarations . length === 1 &&
! ( kind !== "var" && init$1 . declarations [ 0 ] . init ) ) {
if ( this . options . ecmaVersion >= 9 ) {
if ( this . type === types . _in ) {
if ( awaitAt > - 1 ) { this . unexpected ( awaitAt ) ; }
} else { node . await = awaitAt > - 1 ; }
}
return this . parseForIn ( node , init$1 )
}
if ( awaitAt > - 1 ) { this . unexpected ( awaitAt ) ; }
return this . parseFor ( node , init$1 )
}
var refDestructuringErrors = new DestructuringErrors ;
var init = this . parseExpression ( true , refDestructuringErrors ) ;
if ( this . type === types . _in || ( this . options . ecmaVersion >= 6 && this . isContextual ( "of" ) ) ) {
if ( this . options . ecmaVersion >= 9 ) {
if ( this . type === types . _in ) {
if ( awaitAt > - 1 ) { this . unexpected ( awaitAt ) ; }
} else { node . await = awaitAt > - 1 ; }
}
this . toAssignable ( init , false , refDestructuringErrors ) ;
this . checkLVal ( init ) ;
return this . parseForIn ( node , init )
} else {
this . checkExpressionErrors ( refDestructuringErrors , true ) ;
}
if ( awaitAt > - 1 ) { this . unexpected ( awaitAt ) ; }
return this . parseFor ( node , init )
} ;
pp$1 . parseFunctionStatement = function ( node , isAsync ) {
this . next ( ) ;
return this . parseFunction ( node , true , false , isAsync )
} ;
pp$1 . parseIfStatement = function ( node ) {
this . next ( ) ;
node . test = this . parseParenExpression ( ) ;
// allow function declarations in branches, but only in non-strict mode
node . consequent = this . parseStatement ( ! this . strict && this . type == types . _function ) ;
node . alternate = this . eat ( types . _else ) ? this . parseStatement ( ! this . strict && this . type == types . _function ) : null ;
return this . finishNode ( node , "IfStatement" )
} ;
pp$1 . parseReturnStatement = function ( node ) {
if ( ! this . inFunction && ! this . options . allowReturnOutsideFunction )
{ this . raise ( this . start , "'return' outside of function" ) ; }
this . next ( ) ;
// In `return` (and `break`/`continue`), the keywords with
// optional arguments, we eagerly look for a semicolon or the
// possibility to insert one.
if ( this . eat ( types . semi ) || this . insertSemicolon ( ) ) { node . argument = null ; }
else { node . argument = this . parseExpression ( ) ; this . semicolon ( ) ; }
return this . finishNode ( node , "ReturnStatement" )
} ;
pp$1 . parseSwitchStatement = function ( node ) {
var this $1 = this ;
this . next ( ) ;
node . discriminant = this . parseParenExpression ( ) ;
node . cases = [ ] ;
this . expect ( types . braceL ) ;
this . labels . push ( switchLabel ) ;
this . enterLexicalScope ( ) ;
// Statements under must be grouped (by label) in SwitchCase
// nodes. `cur` is used to keep the node that we are currently
// adding statements to.
var cur ;
for ( var sawDefault = false ; this . type != types . braceR ; ) {
if ( this $1 . type === types . _case || this $1 . type === types . _default ) {
var isCase = this $1 . type === types . _case ;
if ( cur ) { this $1 . finishNode ( cur , "SwitchCase" ) ; }
node . cases . push ( cur = this $1 . startNode ( ) ) ;
cur . consequent = [ ] ;
this $1 . next ( ) ;
if ( isCase ) {
cur . test = this $1 . parseExpression ( ) ;
} else {
if ( sawDefault ) { this $1 . raiseRecoverable ( this $1 . lastTokStart , "Multiple default clauses" ) ; }
sawDefault = true ;
cur . test = null ;
}
this $1 . expect ( types . colon ) ;
} else {
if ( ! cur ) { this $1 . unexpected ( ) ; }
cur . consequent . push ( this $1 . parseStatement ( true ) ) ;
}
}
this . exitLexicalScope ( ) ;
if ( cur ) { this . finishNode ( cur , "SwitchCase" ) ; }
this . next ( ) ; // Closing brace
this . labels . pop ( ) ;
return this . finishNode ( node , "SwitchStatement" )
} ;
pp$1 . parseThrowStatement = function ( node ) {
this . next ( ) ;
if ( lineBreak . test ( this . input . slice ( this . lastTokEnd , this . start ) ) )
{ this . raise ( this . lastTokEnd , "Illegal newline after throw" ) ; }
node . argument = this . parseExpression ( ) ;
this . semicolon ( ) ;
return this . finishNode ( node , "ThrowStatement" )
} ;
// Reused empty array added for node fields that are always empty.
var empty = [ ] ;
pp$1 . parseTryStatement = function ( node ) {
this . next ( ) ;
node . block = this . parseBlock ( ) ;
node . handler = null ;
if ( this . type === types . _catch ) {
var clause = this . startNode ( ) ;
this . next ( ) ;
this . expect ( types . parenL ) ;
clause . param = this . parseBindingAtom ( ) ;
this . enterLexicalScope ( ) ;
this . checkLVal ( clause . param , "let" ) ;
this . expect ( types . parenR ) ;
clause . body = this . parseBlock ( false ) ;
this . exitLexicalScope ( ) ;
node . handler = this . finishNode ( clause , "CatchClause" ) ;
}
node . finalizer = this . eat ( types . _finally ) ? this . parseBlock ( ) : null ;
if ( ! node . handler && ! node . finalizer )
{ this . raise ( node . start , "Missing catch or finally clause" ) ; }
return this . finishNode ( node , "TryStatement" )
} ;
pp$1 . parseVarStatement = function ( node , kind ) {
this . next ( ) ;
this . parseVar ( node , false , kind ) ;
this . semicolon ( ) ;
return this . finishNode ( node , "VariableDeclaration" )
} ;
pp$1 . parseWhileStatement = function ( node ) {
this . next ( ) ;
node . test = this . parseParenExpression ( ) ;
this . labels . push ( loopLabel ) ;
node . body = this . parseStatement ( false ) ;
this . labels . pop ( ) ;
return this . finishNode ( node , "WhileStatement" )
} ;
pp$1 . parseWithStatement = function ( node ) {
if ( this . strict ) { this . raise ( this . start , "'with' in strict mode" ) ; }
this . next ( ) ;
node . object = this . parseParenExpression ( ) ;
node . body = this . parseStatement ( false ) ;
return this . finishNode ( node , "WithStatement" )
} ;
pp$1 . parseEmptyStatement = function ( node ) {
this . next ( ) ;
return this . finishNode ( node , "EmptyStatement" )
} ;
pp$1 . parseLabeledStatement = function ( node , maybeName , expr ) {
var this $1 = this ;
for ( var i$1 = 0 , list = this $1 . labels ; i$1 < list . length ; i$1 += 1 )
{
var label = list [ i$1 ] ;
if ( label . name === maybeName )
{ this $1 . raise ( expr . start , "Label '" + maybeName + "' is already declared" ) ;
} }
var kind = this . type . isLoop ? "loop" : this . type === types . _switch ? "switch" : null ;
for ( var i = this . labels . length - 1 ; i >= 0 ; i -- ) {
var label$1 = this $1 . labels [ i ] ;
if ( label$1 . statementStart == node . start ) {
// Update information about previous labels on this node
label$1 . statementStart = this $1 . start ;
label$1 . kind = kind ;
} else { break }
}
this . labels . push ( { name : maybeName , kind : kind , statementStart : this . start } ) ;
node . body = this . parseStatement ( true ) ;
if ( node . body . type == "ClassDeclaration" ||
node . body . type == "VariableDeclaration" && node . body . kind != "var" ||
node . body . type == "FunctionDeclaration" && ( this . strict || node . body . generator ) )
{ this . raiseRecoverable ( node . body . start , "Invalid labeled declaration" ) ; }
this . labels . pop ( ) ;
node . label = expr ;
return this . finishNode ( node , "LabeledStatement" )
} ;
pp$1 . parseExpressionStatement = function ( node , expr ) {
node . expression = expr ;
this . semicolon ( ) ;
return this . finishNode ( node , "ExpressionStatement" )
} ;
// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).
pp$1 . parseBlock = function ( createNewLexicalScope ) {
var this $1 = this ;
if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true ;
var node = this . startNode ( ) ;
node . body = [ ] ;
this . expect ( types . braceL ) ;
if ( createNewLexicalScope ) {
this . enterLexicalScope ( ) ;
}
while ( ! this . eat ( types . braceR ) ) {
var stmt = this $1 . parseStatement ( true ) ;
node . body . push ( stmt ) ;
}
if ( createNewLexicalScope ) {
this . exitLexicalScope ( ) ;
}
return this . finishNode ( node , "BlockStatement" )
} ;
// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.
pp$1 . parseFor = function ( node , init ) {
node . init = init ;
this . expect ( types . semi ) ;
node . test = this . type === types . semi ? null : this . parseExpression ( ) ;
this . expect ( types . semi ) ;
node . update = this . type === types . parenR ? null : this . parseExpression ( ) ;
this . expect ( types . parenR ) ;
this . exitLexicalScope ( ) ;
node . body = this . parseStatement ( false ) ;
this . labels . pop ( ) ;
return this . finishNode ( node , "ForStatement" )
} ;
// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.
pp$1 . parseForIn = function ( node , init ) {
var type = this . type === types . _in ? "ForInStatement" : "ForOfStatement" ;
this . next ( ) ;
if ( type == "ForInStatement" ) {
if ( init . type === "AssignmentPattern" ||
( init . type === "VariableDeclaration" && init . declarations [ 0 ] . init != null &&
( this . strict || init . declarations [ 0 ] . id . type !== "Identifier" ) ) )
{ this . raise ( init . start , "Invalid assignment in for-in loop head" ) ; }
}
node . left = init ;
node . right = type == "ForInStatement" ? this . parseExpression ( ) : this . parseMaybeAssign ( ) ;
this . expect ( types . parenR ) ;
this . exitLexicalScope ( ) ;
node . body = this . parseStatement ( false ) ;
this . labels . pop ( ) ;
return this . finishNode ( node , type )
} ;
// Parse a list of variable declarations.
pp$1 . parseVar = function ( node , isFor , kind ) {
var this $1 = this ;
node . declarations = [ ] ;
node . kind = kind ;
for ( ; ; ) {
var decl = this $1 . startNode ( ) ;
this $1 . parseVarId ( decl , kind ) ;
if ( this $1 . eat ( types . eq ) ) {
decl . init = this $1 . parseMaybeAssign ( isFor ) ;
} else if ( kind === "const" && ! ( this $1 . type === types . _in || ( this $1 . options . ecmaVersion >= 6 && this $1 . isContextual ( "of" ) ) ) ) {
this $1 . unexpected ( ) ;
} else if ( decl . id . type != "Identifier" && ! ( isFor && ( this $1 . type === types . _in || this $1 . isContextual ( "of" ) ) ) ) {
this $1 . raise ( this $1 . lastTokEnd , "Complex binding patterns require an initialization value" ) ;
} else {
decl . init = null ;
}
node . declarations . push ( this $1 . finishNode ( decl , "VariableDeclarator" ) ) ;
if ( ! this $1 . eat ( types . comma ) ) { break }
}
return node
} ;
pp$1 . parseVarId = function ( decl , kind ) {
decl . id = this . parseBindingAtom ( kind ) ;
this . checkLVal ( decl . id , kind , false ) ;
} ;
// Parse a function declaration or literal (depending on the
// `isStatement` parameter).
pp$1 . parseFunction = function ( node , isStatement , allowExpressionBody , isAsync ) {
this . initFunction ( node ) ;
if ( this . options . ecmaVersion >= 9 || this . options . ecmaVersion >= 6 && ! isAsync )
{ node . generator = this . eat ( types . star ) ; }
if ( this . options . ecmaVersion >= 8 )
{ node . async = ! ! isAsync ; }
if ( isStatement ) {
node . id = isStatement === "nullableID" && this . type != types . name ? null : this . parseIdent ( ) ;
if ( node . id ) {
this . checkLVal ( node . id , "var" ) ;
}
}
var oldInGen = this . inGenerator , oldInAsync = this . inAsync ,
oldYieldPos = this . yieldPos , oldAwaitPos = this . awaitPos , oldInFunc = this . inFunction ;
this . inGenerator = node . generator ;
this . inAsync = node . async ;
this . yieldPos = 0 ;
this . awaitPos = 0 ;
this . inFunction = true ;
this . enterFunctionScope ( ) ;
if ( ! isStatement )
{ node . id = this . type == types . name ? this . parseIdent ( ) : null ; }
this . parseFunctionParams ( node ) ;
this . parseFunctionBody ( node , allowExpressionBody ) ;
this . inGenerator = oldInGen ;
this . inAsync = oldInAsync ;
this . yieldPos = oldYieldPos ;
this . awaitPos = oldAwaitPos ;
this . inFunction = oldInFunc ;
return this . finishNode ( node , isStatement ? "FunctionDeclaration" : "FunctionExpression" )
} ;
pp$1 . parseFunctionParams = function ( node ) {
this . expect ( types . parenL ) ;
node . params = this . parseBindingList ( types . parenR , false , this . options . ecmaVersion >= 8 ) ;
this . checkYieldAwaitInDefaultParams ( ) ;
} ;
// Parse a class declaration or literal (depending on the
// `isStatement` parameter).
pp$1 . parseClass = function ( node , isStatement ) {
var this $1 = this ;
this . next ( ) ;
this . parseClassId ( node , isStatement ) ;
this . parseClassSuper ( node ) ;
var classBody = this . startNode ( ) ;
var hadConstructor = false ;
classBody . body = [ ] ;
this . expect ( types . braceL ) ;
while ( ! this . eat ( types . braceR ) ) {
var member = this $1 . parseClassMember ( classBody ) ;
if ( member && member . type === "MethodDefinition" && member . kind === "constructor" ) {
if ( hadConstructor ) { this $1 . raise ( member . start , "Duplicate constructor in the same class" ) ; }
hadConstructor = true ;
}
}
node . body = this . finishNode ( classBody , "ClassBody" ) ;
return this . finishNode ( node , isStatement ? "ClassDeclaration" : "ClassExpression" )
} ;
pp$1 . parseClassMember = function ( classBody ) {
var this $1 = this ;
if ( this . eat ( types . semi ) ) { return null }
var method = this . startNode ( ) ;
var tryContextual = function ( k , noLineBreak ) {
if ( noLineBreak === void 0 ) noLineBreak = false ;
var start = this $1 . start , startLoc = this $1 . startLoc ;
if ( ! this $1 . eatContextual ( k ) ) { return false }
if ( this $1 . type !== types . parenL && ( ! noLineBreak || ! this $1 . canInsertSemicolon ( ) ) ) { return true }
if ( method . key ) { this $1 . unexpected ( ) ; }
method . computed = false ;
method . key = this $1 . startNodeAt ( start , startLoc ) ;
method . key . name = k ;
this $1 . finishNode ( method . key , "Identifier" ) ;
return false
} ;
method . kind = "method" ;
method . static = tryContextual ( "static" ) ;
var isGenerator = this . eat ( types . star ) ;
var isAsync = false ;
if ( ! isGenerator ) {
if ( this . options . ecmaVersion >= 8 && tryContextual ( "async" , true ) ) {
isAsync = true ;
isGenerator = this . options . ecmaVersion >= 9 && this . eat ( types . star ) ;
} else if ( tryContextual ( "get" ) ) {
method . kind = "get" ;
} else if ( tryContextual ( "set" ) ) {
method . kind = "set" ;
}
}
if ( ! method . key ) { this . parsePropertyName ( method ) ; }
var key = method . key ;
if ( ! method . computed && ! method . static && ( key . type === "Identifier" && key . name === "constructor" ||
key . type === "Literal" && key . value === "constructor" ) ) {
if ( method . kind !== "method" ) { this . raise ( key . start , "Constructor can't have get/set modifier" ) ; }
if ( isGenerator ) { this . raise ( key . start , "Constructor can't be a generator" ) ; }
if ( isAsync ) { this . raise ( key . start , "Constructor can't be an async method" ) ; }
method . kind = "constructor" ;
} else if ( method . static && key . type === "Identifier" && key . name === "prototype" ) {
this . raise ( key . start , "Classes may not have a static property named prototype" ) ;
}
this . parseClassMethod ( classBody , method , isGenerator , isAsync ) ;
if ( method . kind === "get" && method . value . params . length !== 0 )
{ this . raiseRecoverable ( method . value . start , "getter should have no params" ) ; }
if ( method . kind === "set" && method . value . params . length !== 1 )
{ this . raiseRecoverable ( method . value . start , "setter should have exactly one param" ) ; }
if ( method . kind === "set" && method . value . params [ 0 ] . type === "RestElement" )
{ this . raiseRecoverable ( method . value . params [ 0 ] . start , "Setter cannot use rest params" ) ; }
return method
} ;
pp$1 . parseClassMethod = function ( classBody , method , isGenerator , isAsync ) {
method . value = this . parseMethod ( isGenerator , isAsync ) ;
classBody . body . push ( this . finishNode ( method , "MethodDefinition" ) ) ;
} ;
pp$1 . parseClassId = function ( node , isStatement ) {
node . id = this . type === types . name ? this . parseIdent ( ) : isStatement === true ? this . unexpected ( ) : null ;
} ;
pp$1 . parseClassSuper = function ( node ) {
node . superClass = this . eat ( types . _extends ) ? this . parseExprSubscripts ( ) : null ;
} ;
// Parses module export declaration.
pp$1 . parseExport = function ( node , exports ) {
var this $1 = this ;
this . next ( ) ;
// export * from '...'
if ( this . eat ( types . star ) ) {
this . expectContextual ( "from" ) ;
if ( this . type !== types . string ) { this . unexpected ( ) ; }
node . source = this . parseExprAtom ( ) ;
this . semicolon ( ) ;
return this . finishNode ( node , "ExportAllDeclaration" )
}
if ( this . eat ( types . _default ) ) { // export default ...
this . checkExport ( exports , "default" , this . lastTokStart ) ;
var isAsync ;
if ( this . type === types . _function || ( isAsync = this . isAsyncFunction ( ) ) ) {
var fNode = this . startNode ( ) ;
this . next ( ) ;
if ( isAsync ) { this . next ( ) ; }
node . declaration = this . parseFunction ( fNode , "nullableID" , false , isAsync ) ;
} else if ( this . type === types . _class ) {
var cNode = this . startNode ( ) ;
node . declaration = this . parseClass ( cNode , "nullableID" ) ;
} else {
node . declaration = this . parseMaybeAssign ( ) ;
this . semicolon ( ) ;
}
return this . finishNode ( node , "ExportDefaultDeclaration" )
}
// export var|const|let|function|class ...
if ( this . shouldParseExportStatement ( ) ) {
node . declaration = this . parseStatement ( true ) ;
if ( node . declaration . type === "VariableDeclaration" )
{ this . checkVariableExport ( exports , node . declaration . declarations ) ; }
else
{ this . checkExport ( exports , node . declaration . id . name , node . declaration . id . start ) ; }
node . specifiers = [ ] ;
node . source = null ;
} else { // export { x, y as z } [from '...']
node . declaration = null ;
node . specifiers = this . parseExportSpecifiers ( exports ) ;
if ( this . eatContextual ( "from" ) ) {
if ( this . type !== types . string ) { this . unexpected ( ) ; }
node . source = this . parseExprAtom ( ) ;
} else {
// check for keywords used as local names
for ( var i = 0 , list = node . specifiers ; i < list . length ; i += 1 ) {
var spec = list [ i ] ;
this $1 . checkUnreserved ( spec . local ) ;
}
node . source = null ;
}
this . semicolon ( ) ;
}
return this . finishNode ( node , "ExportNamedDeclaration" )
} ;
pp$1 . checkExport = function ( exports , name , pos ) {
if ( ! exports ) { return }
if ( has ( exports , name ) )
{ this . raiseRecoverable ( pos , "Duplicate export '" + name + "'" ) ; }
exports [ name ] = true ;
} ;
pp$1 . checkPatternExport = function ( exports , pat ) {
var this $1 = this ;
var type = pat . type ;
if ( type == "Identifier" )
{ this . checkExport ( exports , pat . name , pat . start ) ; }
else if ( type == "ObjectPattern" )
{ for ( var i = 0 , list = pat . properties ; i < list . length ; i += 1 )
{
var prop = list [ i ] ;
this $1 . checkPatternExport ( exports , prop ) ;
} }
else if ( type == "ArrayPattern" )
{ for ( var i$1 = 0 , list$1 = pat . elements ; i$1 < list$1 . length ; i$1 += 1 ) {
var elt = list$1 [ i$1 ] ;
if ( elt ) { this $1 . checkPatternExport ( exports , elt ) ; }
} }
else if ( type == "Property" )
{ this . checkPatternExport ( exports , pat . value ) ; }
else if ( type == "AssignmentPattern" )
{ this . checkPatternExport ( exports , pat . left ) ; }
else if ( type == "RestElement" )
{ this . checkPatternExport ( exports , pat . argument ) ; }
else if ( type == "ParenthesizedExpression" )
{ this . checkPatternExport ( exports , pat . expression ) ; }
} ;
pp$1 . checkVariableExport = function ( exports , decls ) {
var this $1 = this ;
if ( ! exports ) { return }
for ( var i = 0 , list = decls ; i < list . length ; i += 1 )
{
var decl = list [ i ] ;
this $1 . checkPatternExport ( exports , decl . id ) ;
}
} ;
pp$1 . shouldParseExportStatement = function ( ) {
return this . type . keyword === "var" ||
this . type . keyword === "const" ||
this . type . keyword === "class" ||
this . type . keyword === "function" ||
this . isLet ( ) ||
this . isAsyncFunction ( )
} ;
// Parses a comma-separated list of module exports.
pp$1 . parseExportSpecifiers = function ( exports ) {
var this $1 = this ;
var nodes = [ ] , first = true ;
// export { x, y as z } [from '...']
this . expect ( types . braceL ) ;
while ( ! this . eat ( types . braceR ) ) {
if ( ! first ) {
this $1 . expect ( types . comma ) ;
if ( this $1 . afterTrailingComma ( types . braceR ) ) { break }
} else { first = false ; }
var node = this $1 . startNode ( ) ;
node . local = this $1 . parseIdent ( true ) ;
node . exported = this $1 . eatContextual ( "as" ) ? this $1 . parseIdent ( true ) : node . local ;
this $1 . checkExport ( exports , node . exported . name , node . exported . start ) ;
nodes . push ( this $1 . finishNode ( node , "ExportSpecifier" ) ) ;
}
return nodes
} ;
// Parses import declaration.
pp$1 . parseImport = function ( node ) {
this . next ( ) ;
// import '...'
if ( this . type === types . string ) {
node . specifiers = empty ;
node . source = this . parseExprAtom ( ) ;
} else {
node . specifiers = this . parseImportSpecifiers ( ) ;
this . expectContextual ( "from" ) ;
node . source = this . type === types . string ? this . parseExprAtom ( ) : this . unexpected ( ) ;
}
this . semicolon ( ) ;
return this . finishNode ( node , "ImportDeclaration" )
} ;
// Parses a comma-separated list of module imports.
pp$1 . parseImportSpecifiers = function ( ) {
var this $1 = this ;
var nodes = [ ] , first = true ;
if ( this . type === types . name ) {
// import defaultObj, { x, y as z } from '...'
var node = this . startNode ( ) ;
node . local = this . parseIdent ( ) ;
this . checkLVal ( node . local , "let" ) ;
nodes . push ( this . finishNode ( node , "ImportDefaultSpecifier" ) ) ;
if ( ! this . eat ( types . comma ) ) { return nodes }
}
if ( this . type === types . star ) {
var node$1 = this . startNode ( ) ;
this . next ( ) ;
this . expectContextual ( "as" ) ;
node$1 . local = this . parseIdent ( ) ;
this . checkLVal ( node$1 . local , "let" ) ;
nodes . push ( this . finishNode ( node$1 , "ImportNamespaceSpecifier" ) ) ;
return nodes
}
this . expect ( types . braceL ) ;
while ( ! this . eat ( types . braceR ) ) {
if ( ! first ) {
this $1 . expect ( types . comma ) ;
if ( this $1 . afterTrailingComma ( types . braceR ) ) { break }
} else { first = false ; }
var node$2 = this $1 . startNode ( ) ;
node$2 . imported = this $1 . parseIdent ( true ) ;
if ( this $1 . eatContextual ( "as" ) ) {
node$2 . local = this $1 . parseIdent ( ) ;
} else {
this $1 . checkUnreserved ( node$2 . imported ) ;
node$2 . local = node$2 . imported ;
}
this $1 . checkLVal ( node$2 . local , "let" ) ;
nodes . push ( this $1 . finishNode ( node$2 , "ImportSpecifier" ) ) ;
}
return nodes
} ;
// Set `ExpressionStatement#directive` property for directive prologues.
pp$1 . adaptDirectivePrologue = function ( statements ) {
for ( var i = 0 ; i < statements . length && this . isDirectiveCandidate ( statements [ i ] ) ; ++ i ) {
statements [ i ] . directive = statements [ i ] . expression . raw . slice ( 1 , - 1 ) ;
}
} ;
pp$1 . isDirectiveCandidate = function ( statement ) {
return (
statement . type === "ExpressionStatement" &&
statement . expression . type === "Literal" &&
typeof statement . expression . value === "string" &&
// Reject parenthesized strings.
( this . input [ statement . start ] === "\"" || this . input [ statement . start ] === "'" )
)
} ;
var pp$2 = Parser . prototype ;
// Convert existing expression atom to assignable pattern
// if possible.
pp$2 . toAssignable = function ( node , isBinding , refDestructuringErrors ) {
var this $1 = this ;
if ( this . options . ecmaVersion >= 6 && node ) {
switch ( node . type ) {
case "Identifier" :
if ( this . inAsync && node . name === "await" )
{ this . raise ( node . start , "Can not use 'await' as identifier inside an async function" ) ; }
break
case "ObjectPattern" :
case "ArrayPattern" :
case "RestElement" :
break
case "ObjectExpression" :
node . type = "ObjectPattern" ;
if ( refDestructuringErrors ) { this . checkPatternErrors ( refDestructuringErrors , true ) ; }
for ( var i = 0 , list = node . properties ; i < list . length ; i += 1 ) {
var prop = list [ i ] ;
this $1 . toAssignable ( prop , isBinding ) ;
// Early error:
// AssignmentRestProperty[Yield, Await] :
// `...` DestructuringAssignmentTarget[Yield, Await]
//
// It is a Syntax Error if |DestructuringAssignmentTarget| is an |ArrayLiteral| or an |ObjectLiteral|.
if (
prop . type === "RestElement" &&
( prop . argument . type === "ArrayPattern" || prop . argument . type === "ObjectPattern" )
) {
this $1 . raise ( prop . argument . start , "Unexpected token" ) ;
}
}
break
case "Property" :
// AssignmentProperty has type == "Property"
if ( node . kind !== "init" ) { this . raise ( node . key . start , "Object pattern can't contain getter or setter" ) ; }
this . toAssignable ( node . value , isBinding ) ;
break
case "ArrayExpression" :
node . type = "ArrayPattern" ;
if ( refDestructuringErrors ) { this . checkPatternErrors ( refDestructuringErrors , true ) ; }
this . toAssignableList ( node . elements , isBinding ) ;
break
case "SpreadElement" :
node . type = "RestElement" ;
this . toAssignable ( node . argument , isBinding ) ;
if ( node . argument . type === "AssignmentPattern" )
{ this . raise ( node . argument . start , "Rest elements cannot have a default value" ) ; }
break
case "AssignmentExpression" :
if ( node . operator !== "=" ) { this . raise ( node . left . end , "Only '=' operator can be used for specifying default value." ) ; }
node . type = "AssignmentPattern" ;
delete node . operator ;
this . toAssignable ( node . left , isBinding ) ;
// falls through to AssignmentPattern
case "AssignmentPattern" :
break
case "ParenthesizedExpression" :
this . toAssignable ( node . expression , isBinding ) ;
break
case "MemberExpression" :
if ( ! isBinding ) { break }
default :
this . raise ( node . start , "Assigning to rvalue" ) ;
}
} else if ( refDestructuringErrors ) { this . checkPatternErrors ( refDestructuringErrors , true ) ; }
return node
} ;
// Convert list of expression atoms to binding list.
pp$2 . toAssignableList = function ( exprList , isBinding ) {
var this $1 = this ;
var end = exprList . length ;
for ( var i = 0 ; i < end ; i ++ ) {
var elt = exprList [ i ] ;
if ( elt ) { this $1 . toAssignable ( elt , isBinding ) ; }
}
if ( end ) {
var last = exprList [ end - 1 ] ;
if ( this . options . ecmaVersion === 6 && isBinding && last && last . type === "RestElement" && last . argument . type !== "Identifier" )
{ this . unexpected ( last . argument . start ) ; }
}
return exprList
} ;
// Parses spread element.
pp$2 . parseSpread = function ( refDestructuringErrors ) {
var node = this . startNode ( ) ;
this . next ( ) ;
node . argument = this . parseMaybeAssign ( false , refDestructuringErrors ) ;
return this . finishNode ( node , "SpreadElement" )
} ;
pp$2 . parseRestBinding = function ( ) {
var node = this . startNode ( ) ;
this . next ( ) ;
// RestElement inside of a function parameter must be an identifier
if ( this . options . ecmaVersion === 6 && this . type !== types . name )
{ this . unexpected ( ) ; }
node . argument = this . parseBindingAtom ( ) ;
return this . finishNode ( node , "RestElement" )
} ;
// Parses lvalue (assignable) atom.
pp$2 . parseBindingAtom = function ( ) {
if ( this . options . ecmaVersion >= 6 ) {
switch ( this . type ) {
case types . bracketL :
var node = this . startNode ( ) ;
this . next ( ) ;
node . elements = this . parseBindingList ( types . bracketR , true , true ) ;
return this . finishNode ( node , "ArrayPattern" )
case types . braceL :
return this . parseObj ( true )
}
}
return this . parseIdent ( )
} ;
pp$2 . parseBindingList = function ( close , allowEmpty , allowTrailingComma ) {
var this $1 = this ;
var elts = [ ] , first = true ;
while ( ! this . eat ( close ) ) {
if ( first ) { first = false ; }
else { this $1 . expect ( types . comma ) ; }
if ( allowEmpty && this $1 . type === types . comma ) {
elts . push ( null ) ;
} else if ( allowTrailingComma && this $1 . afterTrailingComma ( close ) ) {
break
} else if ( this $1 . type === types . ellipsis ) {
var rest = this $1 . parseRestBinding ( ) ;
this $1 . parseBindingListItem ( rest ) ;
elts . push ( rest ) ;
if ( this $1 . type === types . comma ) { this $1 . raise ( this $1 . start , "Comma is not permitted after the rest element" ) ; }
this $1 . expect ( close ) ;
break
} else {
var elem = this $1 . parseMaybeDefault ( this $1 . start , this $1 . startLoc ) ;
this $1 . parseBindingListItem ( elem ) ;
elts . push ( elem ) ;
}
}
return elts
} ;
pp$2 . parseBindingListItem = function ( param ) {
return param
} ;
// Parses assignment pattern around given atom if possible.
pp$2 . parseMaybeDefault = function ( startPos , startLoc , left ) {
left = left || this . parseBindingAtom ( ) ;
if ( this . options . ecmaVersion < 6 || ! this . eat ( types . eq ) ) { return left }
var node = this . startNodeAt ( startPos , startLoc ) ;
node . left = left ;
node . right = this . parseMaybeAssign ( ) ;
return this . finishNode ( node , "AssignmentPattern" )
} ;
// Verify that a node is an lval — something that can be assigned
// to.
// bindingType can be either:
// 'var' indicating that the lval creates a 'var' binding
// 'let' indicating that the lval creates a lexical ('let' or 'const') binding
// 'none' indicating that the binding should be checked for illegal identifiers, but not for duplicate references
pp$2 . checkLVal = function ( expr , bindingType , checkClashes ) {
var this $1 = this ;
switch ( expr . type ) {
case "Identifier" :
if ( this . strict && this . reservedWordsStrictBind . test ( expr . name ) )
{ this . raiseRecoverable ( expr . start , ( bindingType ? "Binding " : "Assigning to " ) + expr . name + " in strict mode" ) ; }
if ( checkClashes ) {
if ( has ( checkClashes , expr . name ) )
{ this . raiseRecoverable ( expr . start , "Argument name clash" ) ; }
checkClashes [ expr . name ] = true ;
}
if ( bindingType && bindingType !== "none" ) {
if (
bindingType === "var" && ! this . canDeclareVarName ( expr . name ) ||
bindingType !== "var" && ! this . canDeclareLexicalName ( expr . name )
) {
this . raiseRecoverable ( expr . start , ( "Identifier '" + ( expr . name ) + "' has already been declared" ) ) ;
}
if ( bindingType === "var" ) {
this . declareVarName ( expr . name ) ;
} else {
this . declareLexicalName ( expr . name ) ;
}
}
break
case "MemberExpression" :
if ( bindingType ) { this . raiseRecoverable ( expr . start , "Binding member expression" ) ; }
break
case "ObjectPattern" :
for ( var i = 0 , list = expr . properties ; i < list . length ; i += 1 )
{
var prop = list [ i ] ;
this $1 . checkLVal ( prop , bindingType , checkClashes ) ;
}
break
case "Property" :
// AssignmentProperty has type == "Property"
this . checkLVal ( expr . value , bindingType , checkClashes ) ;
break
case "ArrayPattern" :
for ( var i$1 = 0 , list$1 = expr . elements ; i$1 < list$1 . length ; i$1 += 1 ) {
var elem = list$1 [ i$1 ] ;
if ( elem ) { this $1 . checkLVal ( elem , bindingType , checkClashes ) ; }
}
break
case "AssignmentPattern" :
this . checkLVal ( expr . left , bindingType , checkClashes ) ;
break
case "RestElement" :
this . checkLVal ( expr . argument , bindingType , checkClashes ) ;
break
case "ParenthesizedExpression" :
this . checkLVal ( expr . expression , bindingType , checkClashes ) ;
break
default :
this . raise ( expr . start , ( bindingType ? "Binding" : "Assigning to" ) + " rvalue" ) ;
}
} ;
// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
var pp$3 = Parser . prototype ;
// Check if property name clashes with already added.
// Object/class getters and setters are not allowed to clash —
// either with each other or with an init property — and in
// strict mode, init properties are also not allowed to be repeated.
pp$3 . checkPropClash = function ( prop , propHash , refDestructuringErrors ) {
if ( this . options . ecmaVersion >= 9 && prop . type === "SpreadElement" )
{ return }
if ( this . options . ecmaVersion >= 6 && ( prop . computed || prop . method || prop . shorthand ) )
{ return }
var key = prop . key ;
var name ;
switch ( key . type ) {
case "Identifier" : name = key . name ; break
case "Literal" : name = String ( key . value ) ; break
default : return
}
var kind = prop . kind ;
if ( this . options . ecmaVersion >= 6 ) {
if ( name === "__proto__" && kind === "init" ) {
if ( propHash . proto ) {
if ( refDestructuringErrors && refDestructuringErrors . doubleProto < 0 ) { refDestructuringErrors . doubleProto = key . start ; }
// Backwards-compat kludge. Can be removed in version 6.0
else { this . raiseRecoverable ( key . start , "Redefinition of __proto__ property" ) ; }
}
propHash . proto = true ;
}
return
}
name = "$" + name ;
var other = propHash [ name ] ;
if ( other ) {
var redefinition ;
if ( kind === "init" ) {
redefinition = this . strict && other . init || other . get || other . set ;
} else {
redefinition = other . init || other [ kind ] ;
}
if ( redefinition )
{ this . raiseRecoverable ( key . start , "Redefinition of property" ) ; }
} else {
other = propHash [ name ] = {
init : false ,
get : false ,
set : false
} ;
}
other [ kind ] = true ;
} ;
// ### Expression parsing
// These nest, from the most general expression type at the top to
// 'atomic', nondivisible expression types at the bottom. Most of
// the functions will simply let the function(s) below them parse,
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.
// Parse a full expression. The optional arguments are used to
// forbid the `in` operator (in for loops initalization expressions)
// and provide reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).
pp$3 . parseExpression = function ( noIn , refDestructuringErrors ) {
var this $1 = this ;
var startPos = this . start , startLoc = this . startLoc ;
var expr = this . parseMaybeAssign ( noIn , refDestructuringErrors ) ;
if ( this . type === types . comma ) {
var node = this . startNodeAt ( startPos , startLoc ) ;
node . expressions = [ expr ] ;
while ( this . eat ( types . comma ) ) { node . expressions . push ( this $1 . parseMaybeAssign ( noIn , refDestructuringErrors ) ) ; }
return this . finishNode ( node , "SequenceExpression" )
}
return expr
} ;
// Parse an assignment expression. This includes applications of
// operators like `+=`.
pp$3 . parseMaybeAssign = function ( noIn , refDestructuringErrors , afterLeftParse ) {
if ( this . inGenerator && this . isContextual ( "yield" ) ) { return this . parseYield ( ) }
var ownDestructuringErrors = false , oldParenAssign = - 1 , oldTrailingComma = - 1 ;
if ( refDestructuringErrors ) {
oldParenAssign = refDestructuringErrors . parenthesizedAssign ;
oldTrailingComma = refDestructuringErrors . trailingComma ;
refDestructuringErrors . parenthesizedAssign = refDestructuringErrors . trailingComma = - 1 ;
} else {
refDestructuringErrors = new DestructuringErrors ;
ownDestructuringErrors = true ;
}
var startPos = this . start , startLoc = this . startLoc ;
if ( this . type == types . parenL || this . type == types . name )
{ this . potentialArrowAt = this . start ; }
var left = this . parseMaybeConditional ( noIn , refDestructuringErrors ) ;
if ( afterLeftParse ) { left = afterLeftParse . call ( this , left , startPos , startLoc ) ; }
if ( this . type . isAssign ) {
var node = this . startNodeAt ( startPos , startLoc ) ;
node . operator = this . value ;
node . left = this . type === types . eq ? this . toAssignable ( left , false , refDestructuringErrors ) : left ;
if ( ! ownDestructuringErrors ) { DestructuringErrors . call ( refDestructuringErrors ) ; }
refDestructuringErrors . shorthandAssign = - 1 ; // reset because shorthand default was used correctly
this . checkLVal ( left ) ;
this . next ( ) ;
node . right = this . parseMaybeAssign ( noIn ) ;
return this . finishNode ( node , "AssignmentExpression" )
} else {
if ( ownDestructuringErrors ) { this . checkExpressionErrors ( refDestructuringErrors , true ) ; }
}
if ( oldParenAssign > - 1 ) { refDestructuringErrors . parenthesizedAssign = oldParenAssign ; }
if ( oldTrailingComma > - 1 ) { refDestructuringErrors . trailingComma = oldTrailingComma ; }
return left
} ;
// Parse a ternary conditional (`?:`) operator.
pp$3 . parseMaybeConditional = function ( noIn , refDestructuringErrors ) {
var startPos = this . start , startLoc = this . startLoc ;
var expr = this . parseExprOps ( noIn , refDestructuringErrors ) ;
if ( this . checkExpressionErrors ( refDestructuringErrors ) ) { return expr }
if ( this . eat ( types . question ) ) {
var node = this . startNodeAt ( startPos , startLoc ) ;
node . test = expr ;
node . consequent = this . parseMaybeAssign ( ) ;
this . expect ( types . colon ) ;
node . alternate = this . parseMaybeAssign ( noIn ) ;
return this . finishNode ( node , "ConditionalExpression" )
}
return expr
} ;
// Start the precedence parser.
pp$3 . parseExprOps = function ( noIn , refDestructuringErrors ) {
var startPos = this . start , startLoc = this . startLoc ;
var expr = this . parseMaybeUnary ( refDestructuringErrors , false ) ;
if ( this . checkExpressionErrors ( refDestructuringErrors ) ) { return expr }
return expr . start == startPos && expr . type === "ArrowFunctionExpression" ? expr : this . parseExprOp ( expr , startPos , startLoc , - 1 , noIn )
} ;
// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.
pp$3 . parseExprOp = function ( left , leftStartPos , leftStartLoc , minPrec , noIn ) {
var prec = this . type . binop ;
if ( prec != null && ( ! noIn || this . type !== types . _in ) ) {
if ( prec > minPrec ) {
var logical = this . type === types . logicalOR || this . type === types . logicalAND ;
var op = this . value ;
this . next ( ) ;
var startPos = this . start , startLoc = this . startLoc ;
var right = this . parseExprOp ( this . parseMaybeUnary ( null , false ) , startPos , startLoc , prec , noIn ) ;
var node = this . buildBinary ( leftStartPos , leftStartLoc , left , right , op , logical ) ;
return this . parseExprOp ( node , leftStartPos , leftStartLoc , minPrec , noIn )
}
}
return left
} ;
pp$3 . buildBinary = function ( startPos , startLoc , left , right , op , logical ) {
var node = this . startNodeAt ( startPos , startLoc ) ;
node . left = left ;
node . operator = op ;
node . right = right ;
return this . finishNode ( node , logical ? "LogicalExpression" : "BinaryExpression" )
} ;
// Parse unary operators, both prefix and postfix.
pp$3 . parseMaybeUnary = function ( refDestructuringErrors , sawUnary ) {
var this $1 = this ;
var startPos = this . start , startLoc = this . startLoc , expr ;
if ( this . inAsync && this . isContextual ( "await" ) ) {
expr = this . parseAwait ( ) ;
sawUnary = true ;
} else if ( this . type . prefix ) {
var node = this . startNode ( ) , update = this . type === types . incDec ;
node . operator = this . value ;
node . prefix = true ;
this . next ( ) ;
node . argument = this . parseMaybeUnary ( null , true ) ;
this . checkExpressionErrors ( refDestructuringErrors , true ) ;
if ( update ) { this . checkLVal ( node . argument ) ; }
else if ( this . strict && node . operator === "delete" &&
node . argument . type === "Identifier" )
{ this . raiseRecoverable ( node . start , "Deleting local variable in strict mode" ) ; }
else { sawUnary = true ; }
expr = this . finishNode ( node , update ? "UpdateExpression" : "UnaryExpression" ) ;
} else {
expr = this . parseExprSubscripts ( refDestructuringErrors ) ;
if ( this . checkExpressionErrors ( refDestructuringErrors ) ) { return expr }
while ( this . type . postfix && ! this . canInsertSemicolon ( ) ) {
var node$1 = this $1 . startNodeAt ( startPos , startLoc ) ;
node$1 . operator = this $1 . value ;
node$1 . prefix = false ;
node$1 . argument = expr ;
this $1 . checkLVal ( expr ) ;
this $1 . next ( ) ;
expr = this $1 . finishNode ( node$1 , "UpdateExpression" ) ;
}
}
if ( ! sawUnary && this . eat ( types . starstar ) )
{ return this . buildBinary ( startPos , startLoc , expr , this . parseMaybeUnary ( null , false ) , "**" , false ) }
else
{ return expr }
} ;
// Parse call, dot, and `[]`-subscript expressions.
pp$3 . parseExprSubscripts = function ( refDestructuringErrors ) {
var startPos = this . start , startLoc = this . startLoc ;
var expr = this . parseExprAtom ( refDestructuringErrors ) ;
var skipArrowSubscripts = expr . type === "ArrowFunctionExpression" && this . input . slice ( this . lastTokStart , this . lastTokEnd ) !== ")" ;
if ( this . checkExpressionErrors ( refDestructuringErrors ) || skipArrowSubscripts ) { return expr }
var result = this . parseSubscripts ( expr , startPos , startLoc ) ;
if ( refDestructuringErrors && result . type === "MemberExpression" ) {
if ( refDestructuringErrors . parenthesizedAssign >= result . start ) { refDestructuringErrors . parenthesizedAssign = - 1 ; }
if ( refDestructuringErrors . parenthesizedBind >= result . start ) { refDestructuringErrors . parenthesizedBind = - 1 ; }
}
return result
} ;
pp$3 . parseSubscripts = function ( base , startPos , startLoc , noCalls ) {
var this $1 = this ;
var maybeAsyncArrow = this . options . ecmaVersion >= 8 && base . type === "Identifier" && base . name === "async" &&
this . lastTokEnd == base . end && ! this . canInsertSemicolon ( ) && this . input . slice ( base . start , base . end ) === "async" ;
for ( var computed = ( void 0 ) ; ; ) {
if ( ( computed = this $1 . eat ( types . bracketL ) ) || this $1 . eat ( types . dot ) ) {
var node = this $1 . startNodeAt ( startPos , startLoc ) ;
node . object = base ;
node . property = computed ? this $1 . parseExpression ( ) : this $1 . parseIdent ( true ) ;
node . computed = ! ! computed ;
if ( computed ) { this $1 . expect ( types . bracketR ) ; }
base = this $1 . finishNode ( node , "MemberExpression" ) ;
} else if ( ! noCalls && this $1 . eat ( types . parenL ) ) {
var refDestructuringErrors = new DestructuringErrors , oldYieldPos = this $1 . yieldPos , oldAwaitPos = this $1 . awaitPos ;
this $1 . yieldPos = 0 ;
this $1 . awaitPos = 0 ;
var exprList = this $1 . parseExprList ( types . parenR , this $1 . options . ecmaVersion >= 8 , false , refDestructuringErrors ) ;
if ( maybeAsyncArrow && ! this $1 . canInsertSemicolon ( ) && this $1 . eat ( types . arrow ) ) {
this $1 . checkPatternErrors ( refDestructuringErrors , false ) ;
this $1 . checkYieldAwaitInDefaultParams ( ) ;
this $1 . yieldPos = oldYieldPos ;
this $1 . awaitPos = oldAwaitPos ;
return this $1 . parseArrowExpression ( this $1 . startNodeAt ( startPos , startLoc ) , exprList , true )
}
this $1 . checkExpressionErrors ( refDestructuringErrors , true ) ;
this $1 . yieldPos = oldYieldPos || this $1 . yieldPos ;
this $1 . awaitPos = oldAwaitPos || this $1 . awaitPos ;
var node$1 = this $1 . startNodeAt ( startPos , startLoc ) ;
node$1 . callee = base ;
node$1 . arguments = exprList ;
base = this $1 . finishNode ( node$1 , "CallExpression" ) ;
} else if ( this $1 . type === types . backQuote ) {
var node$2 = this $1 . startNodeAt ( startPos , startLoc ) ;
node$2 . tag = base ;
node$2 . quasi = this $1 . parseTemplate ( { isTagged : true } ) ;
base = this $1 . finishNode ( node$2 , "TaggedTemplateExpression" ) ;
} else {
return base
}
}
} ;
// Parse an atomic expression — either a single token that is an
// expression, an expression started by a keyword like `function` or
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.
pp$3 . parseExprAtom = function ( refDestructuringErrors ) {
var node , canBeArrow = this . potentialArrowAt == this . start ;
switch ( this . type ) {
case types . _super :
if ( ! this . inFunction )
{ this . raise ( this . start , "'super' outside of function or class" ) ; }
node = this . startNode ( ) ;
this . next ( ) ;
// The `super` keyword can appear at below:
// SuperProperty:
// super [ Expression ]
// super . IdentifierName
// SuperCall:
// super Arguments
if ( this . type !== types . dot && this . type !== types . bracketL && this . type !== types . parenL )
{ this . unexpected ( ) ; }
return this . finishNode ( node , "Super" )
case types . _this :
node = this . startNode ( ) ;
this . next ( ) ;
return this . finishNode ( node , "ThisExpression" )
case types . name :
var startPos = this . start , startLoc = this . startLoc , containsEsc = this . containsEsc ;
var id = this . parseIdent ( this . type !== types . name ) ;
if ( this . options . ecmaVersion >= 8 && ! containsEsc && id . name === "async" && ! this . canInsertSemicolon ( ) && this . eat ( types . _function ) )
{ return this . parseFunction ( this . startNodeAt ( startPos , startLoc ) , false , false , true ) }
if ( canBeArrow && ! this . canInsertSemicolon ( ) ) {
if ( this . eat ( types . arrow ) )
{ return this . parseArrowExpression ( this . startNodeAt ( startPos , startLoc ) , [ id ] , false ) }
if ( this . options . ecmaVersion >= 8 && id . name === "async" && this . type === types . name && ! containsEsc ) {
id = this . parseIdent ( ) ;
if ( this . canInsertSemicolon ( ) || ! this . eat ( types . arrow ) )
{ this . unexpected ( ) ; }
return this . parseArrowExpression ( this . startNodeAt ( startPos , startLoc ) , [ id ] , true )
}
}
return id
case types . regexp :
var value = this . value ;
node = this . parseLiteral ( value . value ) ;
node . regex = { pattern : value . pattern , flags : value . flags } ;
return node
case types . num : case types . string :
return this . parseLiteral ( this . value )
case types . _null : case types . _true : case types . _false :
node = this . startNode ( ) ;
node . value = this . type === types . _null ? null : this . type === types . _true ;
node . raw = this . type . keyword ;
this . next ( ) ;
return this . finishNode ( node , "Literal" )
case types . parenL :
var start = this . start , expr = this . parseParenAndDistinguishExpression ( canBeArrow ) ;
if ( refDestructuringErrors ) {
if ( refDestructuringErrors . parenthesizedAssign < 0 && ! this . isSimpleAssignTarget ( expr ) )
{ refDestructuringErrors . parenthesizedAssign = start ; }
if ( refDestructuringErrors . parenthesizedBind < 0 )
{ refDestructuringErrors . parenthesizedBind = start ; }
}
return expr
case types . bracketL :
node = this . startNode ( ) ;
this . next ( ) ;
node . elements = this . parseExprList ( types . bracketR , true , true , refDestructuringErrors ) ;
return this . finishNode ( node , "ArrayExpression" )
case types . braceL :
return this . parseObj ( false , refDestructuringErrors )
case types . _function :
node = this . startNode ( ) ;
this . next ( ) ;
return this . parseFunction ( node , false )
case types . _class :
return this . parseClass ( this . startNode ( ) , false )
case types . _new :
return this . parseNew ( )
case types . backQuote :
return this . parseTemplate ( )
default :
this . unexpected ( ) ;
}
} ;
pp$3 . parseLiteral = function ( value ) {
var node = this . startNode ( ) ;
node . value = value ;
node . raw = this . input . slice ( this . start , this . end ) ;
this . next ( ) ;
return this . finishNode ( node , "Literal" )
} ;
pp$3 . parseParenExpression = function ( ) {
this . expect ( types . parenL ) ;
var val = this . parseExpression ( ) ;
this . expect ( types . parenR ) ;
return val
} ;
pp$3 . parseParenAndDistinguishExpression = function ( canBeArrow ) {
var this $1 = this ;
var startPos = this . start , startLoc = this . startLoc , val , allowTrailingComma = this . options . ecmaVersion >= 8 ;
if ( this . options . ecmaVersion >= 6 ) {
this . next ( ) ;
var innerStartPos = this . start , innerStartLoc = this . startLoc ;
var exprList = [ ] , first = true , lastIsComma = false ;
var refDestructuringErrors = new DestructuringErrors , oldYieldPos = this . yieldPos , oldAwaitPos = this . awaitPos , spreadStart ;
this . yieldPos = 0 ;
this . awaitPos = 0 ;
while ( this . type !== types . parenR ) {
first ? first = false : this $1 . expect ( types . comma ) ;
if ( allowTrailingComma && this $1 . afterTrailingComma ( types . parenR , true ) ) {
lastIsComma = true ;
break
} else if ( this $1 . type === types . ellipsis ) {
spreadStart = this $1 . start ;
exprList . push ( this $1 . parseParenItem ( this $1 . parseRestBinding ( ) ) ) ;
if ( this $1 . type === types . comma ) { this $1 . raise ( this $1 . start , "Comma is not permitted after the rest element" ) ; }
break
} else {
exprList . push ( this $1 . parseMaybeAssign ( false , refDestructuringErrors , this $1 . parseParenItem ) ) ;
}
}
var innerEndPos = this . start , innerEndLoc = this . startLoc ;
this . expect ( types . parenR ) ;
if ( canBeArrow && ! this . canInsertSemicolon ( ) && this . eat ( types . arrow ) ) {
this . checkPatternErrors ( refDestructuringErrors , false ) ;
this . checkYieldAwaitInDefaultParams ( ) ;
this . yieldPos = oldYieldPos ;
this . awaitPos = oldAwaitPos ;
return this . parseParenArrowList ( startPos , startLoc , exprList )
}
if ( ! exprList . length || lastIsComma ) { this . unexpected ( this . lastTokStart ) ; }
if ( spreadStart ) { this . unexpected ( spreadStart ) ; }
this . checkExpressionErrors ( refDestructuringErrors , true ) ;
this . yieldPos = oldYieldPos || this . yieldPos ;
this . awaitPos = oldAwaitPos || this . awaitPos ;
if ( exprList . length > 1 ) {
val = this . startNodeAt ( innerStartPos , innerStartLoc ) ;
val . expressions = exprList ;
this . finishNodeAt ( val , "SequenceExpression" , innerEndPos , innerEndLoc ) ;
} else {
val = exprList [ 0 ] ;
}
} else {
val = this . parseParenExpression ( ) ;
}
if ( this . options . preserveParens ) {
var par = this . startNodeAt ( startPos , startLoc ) ;
par . expression = val ;
return this . finishNode ( par , "ParenthesizedExpression" )
} else {
return val
}
} ;
pp$3 . parseParenItem = function ( item ) {
return item
} ;
pp$3 . parseParenArrowList = function ( startPos , startLoc , exprList ) {
return this . parseArrowExpression ( this . startNodeAt ( startPos , startLoc ) , exprList )
} ;
// New's precedence is slightly tricky. It must allow its argument to
// be a `[]` or dot subscript expression, but not a call — at least,
// not without wrapping it in parentheses. Thus, it uses the noCalls
// argument to parseSubscripts to prevent it from consuming the
// argument list.
var empty$1 = [ ] ;
pp$3 . parseNew = function ( ) {
var node = this . startNode ( ) ;
var meta = this . parseIdent ( true ) ;
if ( this . options . ecmaVersion >= 6 && this . eat ( types . dot ) ) {
node . meta = meta ;
var containsEsc = this . containsEsc ;
node . property = this . parseIdent ( true ) ;
if ( node . property . name !== "target" || containsEsc )
{ this . raiseRecoverable ( node . property . start , "The only valid meta property for new is new.target" ) ; }
if ( ! this . inFunction )
{ this . raiseRecoverable ( node . start , "new.target can only be used in functions" ) ; }
return this . finishNode ( node , "MetaProperty" )
}
var startPos = this . start , startLoc = this . startLoc ;
node . callee = this . parseSubscripts ( this . parseExprAtom ( ) , startPos , startLoc , true ) ;
if ( this . eat ( types . parenL ) ) { node . arguments = this . parseExprList ( types . parenR , this . options . ecmaVersion >= 8 , false ) ; }
else { node . arguments = empty$1 ; }
return this . finishNode ( node , "NewExpression" )
} ;
// Parse template expression.
pp$3 . parseTemplateElement = function ( ref ) {
var isTagged = ref . isTagged ;
var elem = this . startNode ( ) ;
if ( this . type === types . invalidTemplate ) {
if ( ! isTagged ) {
this . raiseRecoverable ( this . start , "Bad escape sequence in untagged template literal" ) ;
}
elem . value = {
raw : this . value ,
cooked : null
} ;
} else {
elem . value = {
raw : this . input . slice ( this . start , this . end ) . replace ( /\r\n?/g , "\n" ) ,
cooked : this . value
} ;
}
this . next ( ) ;
elem . tail = this . type === types . backQuote ;
return this . finishNode ( elem , "TemplateElement" )
} ;
pp$3 . parseTemplate = function ( ref ) {
var this $1 = this ;
if ( ref === void 0 ) ref = { } ;
var isTagged = ref . isTagged ; if ( isTagged === void 0 ) isTagged = false ;
var node = this . startNode ( ) ;
this . next ( ) ;
node . expressions = [ ] ;
var curElt = this . parseTemplateElement ( { isTagged : isTagged } ) ;
node . quasis = [ curElt ] ;
while ( ! curElt . tail ) {
this $1 . expect ( types . dollarBraceL ) ;
node . expressions . push ( this $1 . parseExpression ( ) ) ;
this $1 . expect ( types . braceR ) ;
node . quasis . push ( curElt = this $1 . parseTemplateElement ( { isTagged : isTagged } ) ) ;
}
this . next ( ) ;
return this . finishNode ( node , "TemplateLiteral" )
} ;
pp$3 . isAsyncProp = function ( prop ) {
return ! prop . computed && prop . key . type === "Identifier" && prop . key . name === "async" &&
( this . type === types . name || this . type === types . num || this . type === types . string || this . type === types . bracketL || this . type . keyword || ( this . options . ecmaVersion >= 9 && this . type === types . star ) ) &&
! lineBreak . test ( this . input . slice ( this . lastTokEnd , this . start ) )
} ;
// Parse an object literal or binding pattern.
pp$3 . parseObj = function ( isPattern , refDestructuringErrors ) {
var this $1 = this ;
var node = this . startNode ( ) , first = true , propHash = { } ;
node . properties = [ ] ;
this . next ( ) ;
while ( ! this . eat ( types . braceR ) ) {
if ( ! first ) {
this $1 . expect ( types . comma ) ;
if ( this $1 . afterTrailingComma ( types . braceR ) ) { break }
} else { first = false ; }
var prop = this $1 . parseProperty ( isPattern , refDestructuringErrors ) ;
if ( ! isPattern ) { this $1 . checkPropClash ( prop , propHash , refDestructuringErrors ) ; }
node . properties . push ( prop ) ;
}
return this . finishNode ( node , isPattern ? "ObjectPattern" : "ObjectExpression" )
} ;
pp$3 . parseProperty = function ( isPattern , refDestructuringErrors ) {
var prop = this . startNode ( ) , isGenerator , isAsync , startPos , startLoc ;
if ( this . options . ecmaVersion >= 9 && this . eat ( types . ellipsis ) ) {
if ( isPattern ) {
prop . argument = this . parseIdent ( false ) ;
if ( this . type === types . comma ) {
this . raise ( this . start , "Comma is not permitted after the rest element" ) ;
}
return this . finishNode ( prop , "RestElement" )
}
// To disallow parenthesized identifier via `this.toAssignable()`.
if ( this . type === types . parenL && refDestructuringErrors ) {
if ( refDestructuringErrors . parenthesizedAssign < 0 ) {
refDestructuringErrors . parenthesizedAssign = this . start ;
}
if ( refDestructuringErrors . parenthesizedBind < 0 ) {
refDestructuringErrors . parenthesizedBind = this . start ;
}
}
// Parse argument.
prop . argument = this . parseMaybeAssign ( false , refDestructuringErrors ) ;
// To disallow trailing comma via `this.toAssignable()`.
if ( this . type === types . comma && refDestructuringErrors && refDestructuringErrors . trailingComma < 0 ) {
refDestructuringErrors . trailingComma = this . start ;
}
// Finish
return this . finishNode ( prop , "SpreadElement" )
}
if ( this . options . ecmaVersion >= 6 ) {
prop . method = false ;
prop . shorthand = false ;
if ( isPattern || refDestructuringErrors ) {
startPos = this . start ;
startLoc = this . startLoc ;
}
if ( ! isPattern )
{ isGenerator = this . eat ( types . star ) ; }
}
var containsEsc = this . containsEsc ;
this . parsePropertyName ( prop ) ;
if ( ! isPattern && ! containsEsc && this . options . ecmaVersion >= 8 && ! isGenerator && this . isAsyncProp ( prop ) ) {
isAsync = true ;
isGenerator = this . options . ecmaVersion >= 9 && this . eat ( types . star ) ;
this . parsePropertyName ( prop , refDestructuringErrors ) ;
} else {
isAsync = false ;
}
this . parsePropertyValue ( prop , isPattern , isGenerator , isAsync , startPos , startLoc , refDestructuringErrors , containsEsc ) ;
return this . finishNode ( prop , "Property" )
} ;
pp$3 . parsePropertyValue = function ( prop , isPattern , isGenerator , isAsync , startPos , startLoc , refDestructuringErrors , containsEsc ) {
if ( ( isGenerator || isAsync ) && this . type === types . colon )
{ this . unexpected ( ) ; }
if ( this . eat ( types . colon ) ) {
prop . value = isPattern ? this . parseMaybeDefault ( this . start , this . startLoc ) : this . parseMaybeAssign ( false , refDestructuringErrors ) ;
prop . kind = "init" ;
} else if ( this . options . ecmaVersion >= 6 && this . type === types . parenL ) {
if ( isPattern ) { this . unexpected ( ) ; }
prop . kind = "init" ;
prop . method = true ;
prop . value = this . parseMethod ( isGenerator , isAsync ) ;
} else if ( ! isPattern && ! containsEsc &&
this . options . ecmaVersion >= 5 && ! prop . computed && prop . key . type === "Identifier" &&
( prop . key . name === "get" || prop . key . name === "set" ) &&
( this . type != types . comma && this . type != types . braceR ) ) {
if ( isGenerator || isAsync ) { this . unexpected ( ) ; }
prop . kind = prop . key . name ;
this . parsePropertyName ( prop ) ;
prop . value = this . parseMethod ( false ) ;
var paramCount = prop . kind === "get" ? 0 : 1 ;
if ( prop . value . params . length !== paramCount ) {
var start = prop . value . start ;
if ( prop . kind === "get" )
{ this . raiseRecoverable ( start , "getter should have no params" ) ; }
else
{ this . raiseRecoverable ( start , "setter should have exactly one param" ) ; }
} else {
if ( prop . kind === "set" && prop . value . params [ 0 ] . type === "RestElement" )
{ this . raiseRecoverable ( prop . value . params [ 0 ] . start , "Setter cannot use rest params" ) ; }
}
} else if ( this . options . ecmaVersion >= 6 && ! prop . computed && prop . key . type === "Identifier" ) {
this . checkUnreserved ( prop . key ) ;
prop . kind = "init" ;
if ( isPattern ) {
prop . value = this . parseMaybeDefault ( startPos , startLoc , prop . key ) ;
} else if ( this . type === types . eq && refDestructuringErrors ) {
if ( refDestructuringErrors . shorthandAssign < 0 )
{ refDestructuringErrors . shorthandAssign = this . start ; }
prop . value = this . parseMaybeDefault ( startPos , startLoc , prop . key ) ;
} else {
prop . value = prop . key ;
}
prop . shorthand = true ;
} else { this . unexpected ( ) ; }
} ;
pp$3 . parsePropertyName = function ( prop ) {
if ( this . options . ecmaVersion >= 6 ) {
if ( this . eat ( types . bracketL ) ) {
prop . computed = true ;
prop . key = this . parseMaybeAssign ( ) ;
this . expect ( types . bracketR ) ;
return prop . key
} else {
prop . computed = false ;
}
}
return prop . key = this . type === types . num || this . type === types . string ? this . parseExprAtom ( ) : this . parseIdent ( true )
} ;
// Initialize empty function node.
pp$3 . initFunction = function ( node ) {
node . id = null ;
if ( this . options . ecmaVersion >= 6 ) {
node . generator = false ;
node . expression = false ;
}
if ( this . options . ecmaVersion >= 8 )
{ node . async = false ; }
} ;
// Parse object or class method.
pp$3 . parseMethod = function ( isGenerator , isAsync ) {
var node = this . startNode ( ) , oldInGen = this . inGenerator , oldInAsync = this . inAsync ,
oldYieldPos = this . yieldPos , oldAwaitPos = this . awaitPos , oldInFunc = this . inFunction ;
this . initFunction ( node ) ;
if ( this . options . ecmaVersion >= 6 )
{ node . generator = isGenerator ; }
if ( this . options . ecmaVersion >= 8 )
{ node . async = ! ! isAsync ; }
this . inGenerator = node . generator ;
this . inAsync = node . async ;
this . yieldPos = 0 ;
this . awaitPos = 0 ;
this . inFunction = true ;
this . enterFunctionScope ( ) ;
this . expect ( types . parenL ) ;
node . params = this . parseBindingList ( types . parenR , false , this . options . ecmaVersion >= 8 ) ;
this . checkYieldAwaitInDefaultParams ( ) ;
this . parseFunctionBody ( node , false ) ;
this . inGenerator = oldInGen ;
this . inAsync = oldInAsync ;
this . yieldPos = oldYieldPos ;
this . awaitPos = oldAwaitPos ;
this . inFunction = oldInFunc ;
return this . finishNode ( node , "FunctionExpression" )
} ;
// Parse arrow function expression with given parameters.
pp$3 . parseArrowExpression = function ( node , params , isAsync ) {
var oldInGen = this . inGenerator , oldInAsync = this . inAsync ,
oldYieldPos = this . yieldPos , oldAwaitPos = this . awaitPos , oldInFunc = this . inFunction ;
this . enterFunctionScope ( ) ;
this . initFunction ( node ) ;
if ( this . options . ecmaVersion >= 8 )
{ node . async = ! ! isAsync ; }
this . inGenerator = false ;
this . inAsync = node . async ;
this . yieldPos = 0 ;
this . awaitPos = 0 ;
this . inFunction = true ;
node . params = this . toAssignableList ( params , true ) ;
this . parseFunctionBody ( node , true ) ;
this . inGenerator = oldInGen ;
this . inAsync = oldInAsync ;
this . yieldPos = oldYieldPos ;
this . awaitPos = oldAwaitPos ;
this . inFunction = oldInFunc ;
return this . finishNode ( node , "ArrowFunctionExpression" )
} ;
// Parse function body and check parameters.
pp$3 . parseFunctionBody = function ( node , isArrowFunction ) {
var isExpression = isArrowFunction && this . type !== types . braceL ;
var oldStrict = this . strict , useStrict = false ;
if ( isExpression ) {
node . body = this . parseMaybeAssign ( ) ;
node . expression = true ;
this . checkParams ( node , false ) ;
} else {
var nonSimple = this . options . ecmaVersion >= 7 && ! this . isSimpleParamList ( node . params ) ;
if ( ! oldStrict || nonSimple ) {
useStrict = this . strictDirective ( this . end ) ;
// If this is a strict mode function, verify that argument names
// are not repeated, and it does not try to bind the words `eval`
// or `arguments`.
if ( useStrict && nonSimple )
{ this . raiseRecoverable ( node . start , "Illegal 'use strict' directive in function with non-simple parameter list" ) ; }
}
// Start a new scope with regard to labels and the `inFunction`
// flag (restore them to their old value afterwards).
var oldLabels = this . labels ;
this . labels = [ ] ;
if ( useStrict ) { this . strict = true ; }
// Add the params to varDeclaredNames to ensure that an error is thrown
// if a let/const declaration in the function clashes with one of the params.
this . checkParams ( node , ! oldStrict && ! useStrict && ! isArrowFunction && this . isSimpleParamList ( node . params ) ) ;
node . body = this . parseBlock ( false ) ;
node . expression = false ;
this . adaptDirectivePrologue ( node . body . body ) ;
this . labels = oldLabels ;
}
this . exitFunctionScope ( ) ;
if ( this . strict && node . id ) {
// Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
this . checkLVal ( node . id , "none" ) ;
}
this . strict = oldStrict ;
} ;
pp$3 . isSimpleParamList = function ( params ) {
for ( var i = 0 , list = params ; i < list . length ; i += 1 )
{
var param = list [ i ] ;
if ( param . type !== "Identifier" ) { return false
} }
return true
} ;
// Checks function params for various disallowed patterns such as using "eval"
// or "arguments" and duplicate parameters.
pp$3 . checkParams = function ( node , allowDuplicates ) {
var this $1 = this ;
var nameHash = { } ;
for ( var i = 0 , list = node . params ; i < list . length ; i += 1 )
{
var param = list [ i ] ;
this $1 . checkLVal ( param , "var" , allowDuplicates ? null : nameHash ) ;
}
} ;
// Parses a comma-separated list of expressions, and returns them as
// an array. `close` is the token type that ends the list, and
// `allowEmpty` can be turned on to allow subsequent commas with
// nothing in between them to be parsed as `null` (which is needed
// for array literals).
pp$3 . parseExprList = function ( close , allowTrailingComma , allowEmpty , refDestructuringErrors ) {
var this $1 = this ;
var elts = [ ] , first = true ;
while ( ! this . eat ( close ) ) {
if ( ! first ) {
this $1 . expect ( types . comma ) ;
if ( allowTrailingComma && this $1 . afterTrailingComma ( close ) ) { break }
} else { first = false ; }
var elt = ( void 0 ) ;
if ( allowEmpty && this $1 . type === types . comma )
{ elt = null ; }
else if ( this $1 . type === types . ellipsis ) {
elt = this $1 . parseSpread ( refDestructuringErrors ) ;
if ( refDestructuringErrors && this $1 . type === types . comma && refDestructuringErrors . trailingComma < 0 )
{ refDestructuringErrors . trailingComma = this $1 . start ; }
} else {
elt = this $1 . parseMaybeAssign ( false , refDestructuringErrors ) ;
}
elts . push ( elt ) ;
}
return elts
} ;
pp$3 . checkUnreserved = function ( ref ) {
var start = ref . start ;
var end = ref . end ;
var name = ref . name ;
if ( this . inGenerator && name === "yield" )
{ this . raiseRecoverable ( start , "Can not use 'yield' as identifier inside a generator" ) ; }
if ( this . inAsync && name === "await" )
{ this . raiseRecoverable ( start , "Can not use 'await' as identifier inside an async function" ) ; }
if ( this . isKeyword ( name ) )
{ this . raise ( start , ( "Unexpected keyword '" + name + "'" ) ) ; }
if ( this . options . ecmaVersion < 6 &&
this . input . slice ( start , end ) . indexOf ( "\\" ) != - 1 ) { return }
var re = this . strict ? this . reservedWordsStrict : this . reservedWords ;
if ( re . test ( name ) ) {
if ( ! this . inAsync && name === "await" )
{ this . raiseRecoverable ( start , "Can not use keyword 'await' outside an async function" ) ; }
this . raiseRecoverable ( start , ( "The keyword '" + name + "' is reserved" ) ) ;
}
} ;
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp$3 . parseIdent = function ( liberal , isBinding ) {
var node = this . startNode ( ) ;
if ( liberal && this . options . allowReserved == "never" ) { liberal = false ; }
if ( this . type === types . name ) {
node . name = this . value ;
} else if ( this . type . keyword ) {
node . name = this . type . keyword ;
// To fix https://github.com/acornjs/acorn/issues/575
// `class` and `function` keywords push new context into this.context.
// But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.
// If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword
if ( ( node . name === "class" || node . name === "function" ) &&
( this . lastTokEnd !== this . lastTokStart + 1 || this . input . charCodeAt ( this . lastTokStart ) !== 46 ) ) {
this . context . pop ( ) ;
}
} else {
this . unexpected ( ) ;
}
this . next ( ) ;
this . finishNode ( node , "Identifier" ) ;
if ( ! liberal ) { this . checkUnreserved ( node ) ; }
return node
} ;
// Parses yield expression inside generator.
pp$3 . parseYield = function ( ) {
if ( ! this . yieldPos ) { this . yieldPos = this . start ; }
var node = this . startNode ( ) ;
this . next ( ) ;
if ( this . type == types . semi || this . canInsertSemicolon ( ) || ( this . type != types . star && ! this . type . startsExpr ) ) {
node . delegate = false ;
node . argument = null ;
} else {
node . delegate = this . eat ( types . star ) ;
node . argument = this . parseMaybeAssign ( ) ;
}
return this . finishNode ( node , "YieldExpression" )
} ;
pp$3 . parseAwait = function ( ) {
if ( ! this . awaitPos ) { this . awaitPos = this . start ; }
var node = this . startNode ( ) ;
this . next ( ) ;
node . argument = this . parseMaybeUnary ( null , true ) ;
return this . finishNode ( node , "AwaitExpression" )
} ;
var pp$4 = Parser . prototype ;
// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.
pp$4 . raise = function ( pos , message ) {
var loc = getLineInfo ( this . input , pos ) ;
message += " (" + loc . line + ":" + loc . column + ")" ;
var err = new SyntaxError ( message ) ;
err . pos = pos ; err . loc = loc ; err . raisedAt = this . pos ;
throw err
} ;
pp$4 . raiseRecoverable = pp$4 . raise ;
pp$4 . curPosition = function ( ) {
if ( this . options . locations ) {
return new Position ( this . curLine , this . pos - this . lineStart )
}
} ;
var pp$5 = Parser . prototype ;
// Object.assign polyfill
var assign = Object . assign || function ( target ) {
var sources = [ ] , len = arguments . length - 1 ;
while ( len -- > 0 ) sources [ len ] = arguments [ len + 1 ] ;
for ( var i = 0 , list = sources ; i < list . length ; i += 1 ) {
var source = list [ i ] ;
for ( var key in source ) {
if ( has ( source , key ) ) {
target [ key ] = source [ key ] ;
}
}
}
return target
} ;
// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.
pp$5 . enterFunctionScope = function ( ) {
// var: a hash of var-declared names in the current lexical scope
// lexical: a hash of lexically-declared names in the current lexical scope
// childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope)
// parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope)
this . scopeStack . push ( { var : { } , lexical : { } , childVar : { } , parentLexical : { } } ) ;
} ;
pp$5 . exitFunctionScope = function ( ) {
this . scopeStack . pop ( ) ;
} ;
pp$5 . enterLexicalScope = function ( ) {
var parentScope = this . scopeStack [ this . scopeStack . length - 1 ] ;
var childScope = { var : { } , lexical : { } , childVar : { } , parentLexical : { } } ;
this . scopeStack . push ( childScope ) ;
assign ( childScope . parentLexical , parentScope . lexical , parentScope . parentLexical ) ;
} ;
pp$5 . exitLexicalScope = function ( ) {
var childScope = this . scopeStack . pop ( ) ;
var parentScope = this . scopeStack [ this . scopeStack . length - 1 ] ;
assign ( parentScope . childVar , childScope . var , childScope . childVar ) ;
} ;
/ * *
* A name can be declared with ` var ` if there are no variables with the same name declared with ` let ` / ` const `
* in the current lexical scope or any of the parent lexical scopes in this function .
* /
pp$5 . canDeclareVarName = function ( name ) {
var currentScope = this . scopeStack [ this . scopeStack . length - 1 ] ;
return ! has ( currentScope . lexical , name ) && ! has ( currentScope . parentLexical , name )
} ;
/ * *
* A name can be declared with ` let ` / ` const ` if there are no variables with the same name declared with ` let ` / ` const `
* in the current scope , and there are no variables with the same name declared with ` var ` in the current scope or in
* any child lexical scopes in this function .
* /
pp$5 . canDeclareLexicalName = function ( name ) {
var currentScope = this . scopeStack [ this . scopeStack . length - 1 ] ;
return ! has ( currentScope . lexical , name ) && ! has ( currentScope . var , name ) && ! has ( currentScope . childVar , name )
} ;
pp$5 . declareVarName = function ( name ) {
this . scopeStack [ this . scopeStack . length - 1 ] . var [ name ] = true ;
} ;
pp$5 . declareLexicalName = function ( name ) {
this . scopeStack [ this . scopeStack . length - 1 ] . lexical [ name ] = true ;
} ;
var Node = function Node ( parser , pos , loc ) {
this . type = "" ;
this . start = pos ;
this . end = 0 ;
if ( parser . options . locations )
{ this . loc = new SourceLocation ( parser , loc ) ; }
if ( parser . options . directSourceFile )
{ this . sourceFile = parser . options . directSourceFile ; }
if ( parser . options . ranges )
{ this . range = [ pos , 0 ] ; }
} ;
// Start an AST node, attaching a start offset.
var pp$6 = Parser . prototype ;
pp$6 . startNode = function ( ) {
return new Node ( this , this . start , this . startLoc )
} ;
pp$6 . startNodeAt = function ( pos , loc ) {
return new Node ( this , pos , loc )
} ;
// Finish an AST node, adding `type` and `end` properties.
function finishNodeAt ( node , type , pos , loc ) {
node . type = type ;
node . end = pos ;
if ( this . options . locations )
{ node . loc . end = loc ; }
if ( this . options . ranges )
{ node . range [ 1 ] = pos ; }
return node
}
pp$6 . finishNode = function ( node , type ) {
return finishNodeAt . call ( this , node , type , this . lastTokEnd , this . lastTokEndLoc )
} ;
// Finish node at given position
pp$6 . finishNodeAt = function ( node , type , pos , loc ) {
return finishNodeAt . call ( this , node , type , pos , loc )
} ;
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
var TokContext = function TokContext ( token , isExpr , preserveSpace , override , generator ) {
this . token = token ;
this . isExpr = ! ! isExpr ;
this . preserveSpace = ! ! preserveSpace ;
this . override = override ;
this . generator = ! ! generator ;
} ;
var types$1 = {
b _stat : new TokContext ( "{" , false ) ,
b _expr : new TokContext ( "{" , true ) ,
b _tmpl : new TokContext ( "${" , false ) ,
p _stat : new TokContext ( "(" , false ) ,
p _expr : new TokContext ( "(" , true ) ,
q _tmpl : new TokContext ( "`" , true , true , function ( p ) { return p . tryReadTemplateToken ( ) ; } ) ,
f _stat : new TokContext ( "function" , false ) ,
f _expr : new TokContext ( "function" , true ) ,
f _expr _gen : new TokContext ( "function" , true , false , null , true ) ,
f _gen : new TokContext ( "function" , false , false , null , true )
} ;
var pp$7 = Parser . prototype ;
pp$7 . initialContext = function ( ) {
return [ types$1 . b _stat ]
} ;
pp$7 . braceIsBlock = function ( prevType ) {
var parent = this . curContext ( ) ;
if ( parent === types$1 . f _expr || parent === types$1 . f _stat )
{ return true }
if ( prevType === types . colon && ( parent === types$1 . b _stat || parent === types$1 . b _expr ) )
{ return ! parent . isExpr }
// The check for `tt.name && exprAllowed` detects whether we are
// after a `yield` or `of` construct. See the `updateContext` for
// `tt.name`.
if ( prevType === types . _return || prevType == types . name && this . exprAllowed )
{ return lineBreak . test ( this . input . slice ( this . lastTokEnd , this . start ) ) }
if ( prevType === types . _else || prevType === types . semi || prevType === types . eof || prevType === types . parenR || prevType == types . arrow )
{ return true }
if ( prevType == types . braceL )
{ return parent === types$1 . b _stat }
if ( prevType == types . _var || prevType == types . name )
{ return false }
return ! this . exprAllowed
} ;
pp$7 . inGeneratorContext = function ( ) {
var this $1 = this ;
for ( var i = this . context . length - 1 ; i >= 1 ; i -- ) {
var context = this $1 . context [ i ] ;
if ( context . token === "function" )
{ return context . generator }
}
return false
} ;
pp$7 . updateContext = function ( prevType ) {
var update , type = this . type ;
if ( type . keyword && prevType == types . dot )
{ this . exprAllowed = false ; }
else if ( update = type . updateContext )
{ update . call ( this , prevType ) ; }
else
{ this . exprAllowed = type . beforeExpr ; }
} ;
// Token-specific context update code
types . parenR . updateContext = types . braceR . updateContext = function ( ) {
if ( this . context . length == 1 ) {
this . exprAllowed = true ;
return
}
var out = this . context . pop ( ) ;
if ( out === types$1 . b _stat && this . curContext ( ) . token === "function" ) {
out = this . context . pop ( ) ;
}
this . exprAllowed = ! out . isExpr ;
} ;
types . braceL . updateContext = function ( prevType ) {
this . context . push ( this . braceIsBlock ( prevType ) ? types$1 . b _stat : types$1 . b _expr ) ;
this . exprAllowed = true ;
} ;
types . dollarBraceL . updateContext = function ( ) {
this . context . push ( types$1 . b _tmpl ) ;
this . exprAllowed = true ;
} ;
types . parenL . updateContext = function ( prevType ) {
var statementParens = prevType === types . _if || prevType === types . _for || prevType === types . _with || prevType === types . _while ;
this . context . push ( statementParens ? types$1 . p _stat : types$1 . p _expr ) ;
this . exprAllowed = true ;
} ;
types . incDec . updateContext = function ( ) {
// tokExprAllowed stays unchanged
} ;
types . _function . updateContext = types . _class . updateContext = function ( prevType ) {
if ( prevType . beforeExpr && prevType !== types . semi && prevType !== types . _else &&
! ( ( prevType === types . colon || prevType === types . braceL ) && this . curContext ( ) === types$1 . b _stat ) )
{ this . context . push ( types$1 . f _expr ) ; }
else
{ this . context . push ( types$1 . f _stat ) ; }
this . exprAllowed = false ;
} ;
types . backQuote . updateContext = function ( ) {
if ( this . curContext ( ) === types$1 . q _tmpl )
{ this . context . pop ( ) ; }
else
{ this . context . push ( types$1 . q _tmpl ) ; }
this . exprAllowed = false ;
} ;
types . star . updateContext = function ( prevType ) {
if ( prevType == types . _function ) {
var index = this . context . length - 1 ;
if ( this . context [ index ] === types$1 . f _expr )
{ this . context [ index ] = types$1 . f _expr _gen ; }
else
{ this . context [ index ] = types$1 . f _gen ; }
}
this . exprAllowed = true ;
} ;
types . name . updateContext = function ( prevType ) {
var allowed = false ;
if ( this . options . ecmaVersion >= 6 ) {
if ( this . value == "of" && ! this . exprAllowed ||
this . value == "yield" && this . inGeneratorContext ( ) )
{ allowed = true ; }
}
this . exprAllowed = allowed ;
} ;
// Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.
var Token = function Token ( p ) {
this . type = p . type ;
this . value = p . value ;
this . start = p . start ;
this . end = p . end ;
if ( p . options . locations )
{ this . loc = new SourceLocation ( p , p . startLoc , p . endLoc ) ; }
if ( p . options . ranges )
{ this . range = [ p . start , p . end ] ; }
} ;
// ## Tokenizer
var pp$8 = Parser . prototype ;
// Are we running under Rhino?
var isRhino = typeof Packages == "object" && Object . prototype . toString . call ( Packages ) == "[object JavaPackage]" ;
// Move to the next token
pp$8 . next = function ( ) {
if ( this . options . onToken )
{ this . options . onToken ( new Token ( this ) ) ; }
this . lastTokEnd = this . end ;
this . lastTokStart = this . start ;
this . lastTokEndLoc = this . endLoc ;
this . lastTokStartLoc = this . startLoc ;
this . nextToken ( ) ;
} ;
pp$8 . getToken = function ( ) {
this . next ( ) ;
return new Token ( this )
} ;
// If we're in an ES6 environment, make parsers iterable
if ( typeof Symbol !== "undefined" )
{ pp$8 [ Symbol . iterator ] = function ( ) {
var this $1 = this ;
return {
next : function ( ) {
var token = this $1 . getToken ( ) ;
return {
done : token . type === types . eof ,
value : token
}
}
}
} ; }
// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).
pp$8 . curContext = function ( ) {
return this . context [ this . context . length - 1 ]
} ;
// Read a single token, updating the parser object's token-related
// properties.
pp$8 . nextToken = function ( ) {
var curContext = this . curContext ( ) ;
if ( ! curContext || ! curContext . preserveSpace ) { this . skipSpace ( ) ; }
this . start = this . pos ;
if ( this . options . locations ) { this . startLoc = this . curPosition ( ) ; }
if ( this . pos >= this . input . length ) { return this . finishToken ( types . eof ) }
if ( curContext . override ) { return curContext . override ( this ) }
else { this . readToken ( this . fullCharCodeAtPos ( ) ) ; }
} ;
pp$8 . readToken = function ( code ) {
// Identifier or keyword. '\uXXXX' sequences are allowed in
// identifiers, so '\' also dispatches to that.
if ( isIdentifierStart ( code , this . options . ecmaVersion >= 6 ) || code === 92 /* '\' */ )
{ return this . readWord ( ) }
return this . getTokenFromCode ( code )
} ;
pp$8 . fullCharCodeAtPos = function ( ) {
var code = this . input . charCodeAt ( this . pos ) ;
if ( code <= 0xd7ff || code >= 0xe000 ) { return code }
var next = this . input . charCodeAt ( this . pos + 1 ) ;
return ( code << 10 ) + next - 0x35fdc00
} ;
pp$8 . skipBlockComment = function ( ) {
var this $1 = this ;
var startLoc = this . options . onComment && this . curPosition ( ) ;
var start = this . pos , end = this . input . indexOf ( "*/" , this . pos += 2 ) ;
if ( end === - 1 ) { this . raise ( this . pos - 2 , "Unterminated comment" ) ; }
this . pos = end + 2 ;
if ( this . options . locations ) {
lineBreakG . lastIndex = start ;
var match ;
while ( ( match = lineBreakG . exec ( this . input ) ) && match . index < this . pos ) {
++ this $1 . curLine ;
this $1 . lineStart = match . index + match [ 0 ] . length ;
}
}
if ( this . options . onComment )
{ this . options . onComment ( true , this . input . slice ( start + 2 , end ) , start , this . pos ,
startLoc , this . curPosition ( ) ) ; }
} ;
pp$8 . skipLineComment = function ( startSkip ) {
var this $1 = this ;
var start = this . pos ;
var startLoc = this . options . onComment && this . curPosition ( ) ;
var ch = this . input . charCodeAt ( this . pos += startSkip ) ;
while ( this . pos < this . input . length && ! isNewLine ( ch ) ) {
ch = this $1 . input . charCodeAt ( ++ this $1 . pos ) ;
}
if ( this . options . onComment )
{ this . options . onComment ( false , this . input . slice ( start + startSkip , this . pos ) , start , this . pos ,
startLoc , this . curPosition ( ) ) ; }
} ;
// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.
pp$8 . skipSpace = function ( ) {
var this $1 = this ;
loop : while ( this . pos < this . input . length ) {
var ch = this $1 . input . charCodeAt ( this $1 . pos ) ;
switch ( ch ) {
case 32 : case 160 : // ' '
++ this $1 . pos ;
break
case 13 :
if ( this $1 . input . charCodeAt ( this $1 . pos + 1 ) === 10 ) {
++ this $1 . pos ;
}
case 10 : case 8232 : case 8233 :
++ this $1 . pos ;
if ( this $1 . options . locations ) {
++ this $1 . curLine ;
this $1 . lineStart = this $1 . pos ;
}
break
case 47 : // '/'
switch ( this $1 . input . charCodeAt ( this $1 . pos + 1 ) ) {
case 42 : // '*'
this $1 . skipBlockComment ( ) ;
break
case 47 :
this $1 . skipLineComment ( 2 ) ;
break
default :
break loop
}
break
default :
if ( ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace . test ( String . fromCharCode ( ch ) ) ) {
++ this $1 . pos ;
} else {
break loop
}
}
}
} ;
// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
// the token, so that the next one's `start` will point at the
// right position.
pp$8 . finishToken = function ( type , val ) {
this . end = this . pos ;
if ( this . options . locations ) { this . endLoc = this . curPosition ( ) ; }
var prevType = this . type ;
this . type = type ;
this . value = val ;
this . updateContext ( prevType ) ;
} ;
// ### Token reading
// This is the function that is called to fetch the next token. It
// is somewhat obscure, because it works in character codes rather
// than characters, and because operator parsing has been inlined
// into it.
//
// All in the name of speed.
//
pp$8 . readToken _dot = function ( ) {
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next >= 48 && next <= 57 ) { return this . readNumber ( true ) }
var next2 = this . input . charCodeAt ( this . pos + 2 ) ;
if ( this . options . ecmaVersion >= 6 && next === 46 && next2 === 46 ) { // 46 = dot '.'
this . pos += 3 ;
return this . finishToken ( types . ellipsis )
} else {
++ this . pos ;
return this . finishToken ( types . dot )
}
} ;
pp$8 . readToken _slash = function ( ) { // '/'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( this . exprAllowed ) { ++ this . pos ; return this . readRegexp ( ) }
if ( next === 61 ) { return this . finishOp ( types . assign , 2 ) }
return this . finishOp ( types . slash , 1 )
} ;
pp$8 . readToken _mult _modulo _exp = function ( code ) { // '%*'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
var size = 1 ;
var tokentype = code === 42 ? types . star : types . modulo ;
// exponentiation operator ** and **=
if ( this . options . ecmaVersion >= 7 && code == 42 && next === 42 ) {
++ size ;
tokentype = types . starstar ;
next = this . input . charCodeAt ( this . pos + 2 ) ;
}
if ( next === 61 ) { return this . finishOp ( types . assign , size + 1 ) }
return this . finishOp ( tokentype , size )
} ;
pp$8 . readToken _pipe _amp = function ( code ) { // '|&'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next === code ) { return this . finishOp ( code === 124 ? types . logicalOR : types . logicalAND , 2 ) }
if ( next === 61 ) { return this . finishOp ( types . assign , 2 ) }
return this . finishOp ( code === 124 ? types . bitwiseOR : types . bitwiseAND , 1 )
} ;
pp$8 . readToken _caret = function ( ) { // '^'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next === 61 ) { return this . finishOp ( types . assign , 2 ) }
return this . finishOp ( types . bitwiseXOR , 1 )
} ;
pp$8 . readToken _plus _min = function ( code ) { // '+-'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next === code ) {
if ( next == 45 && ! this . inModule && this . input . charCodeAt ( this . pos + 2 ) == 62 &&
( this . lastTokEnd === 0 || lineBreak . test ( this . input . slice ( this . lastTokEnd , this . pos ) ) ) ) {
// A `-->` line comment
this . skipLineComment ( 3 ) ;
this . skipSpace ( ) ;
return this . nextToken ( )
}
return this . finishOp ( types . incDec , 2 )
}
if ( next === 61 ) { return this . finishOp ( types . assign , 2 ) }
return this . finishOp ( types . plusMin , 1 )
} ;
pp$8 . readToken _lt _gt = function ( code ) { // '<>'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
var size = 1 ;
if ( next === code ) {
size = code === 62 && this . input . charCodeAt ( this . pos + 2 ) === 62 ? 3 : 2 ;
if ( this . input . charCodeAt ( this . pos + size ) === 61 ) { return this . finishOp ( types . assign , size + 1 ) }
return this . finishOp ( types . bitShift , size )
}
if ( next == 33 && code == 60 && ! this . inModule && this . input . charCodeAt ( this . pos + 2 ) == 45 &&
this . input . charCodeAt ( this . pos + 3 ) == 45 ) {
// `<!--`, an XML-style comment that should be interpreted as a line comment
this . skipLineComment ( 4 ) ;
this . skipSpace ( ) ;
return this . nextToken ( )
}
if ( next === 61 ) { size = 2 ; }
return this . finishOp ( types . relational , size )
} ;
pp$8 . readToken _eq _excl = function ( code ) { // '=!'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next === 61 ) { return this . finishOp ( types . equality , this . input . charCodeAt ( this . pos + 2 ) === 61 ? 3 : 2 ) }
if ( code === 61 && next === 62 && this . options . ecmaVersion >= 6 ) { // '=>'
this . pos += 2 ;
return this . finishToken ( types . arrow )
}
return this . finishOp ( code === 61 ? types . eq : types . prefix , 1 )
} ;
pp$8 . getTokenFromCode = function ( code ) {
switch ( code ) {
// The interpretation of a dot depends on whether it is followed
// by a digit or another two dots.
case 46 : // '.'
return this . readToken _dot ( )
// Punctuation tokens.
case 40 : ++ this . pos ; return this . finishToken ( types . parenL )
case 41 : ++ this . pos ; return this . finishToken ( types . parenR )
case 59 : ++ this . pos ; return this . finishToken ( types . semi )
case 44 : ++ this . pos ; return this . finishToken ( types . comma )
case 91 : ++ this . pos ; return this . finishToken ( types . bracketL )
case 93 : ++ this . pos ; return this . finishToken ( types . bracketR )
case 123 : ++ this . pos ; return this . finishToken ( types . braceL )
case 125 : ++ this . pos ; return this . finishToken ( types . braceR )
case 58 : ++ this . pos ; return this . finishToken ( types . colon )
case 63 : ++ this . pos ; return this . finishToken ( types . question )
case 96 : // '`'
if ( this . options . ecmaVersion < 6 ) { break }
++ this . pos ;
return this . finishToken ( types . backQuote )
case 48 : // '0'
var next = this . input . charCodeAt ( this . pos + 1 ) ;
if ( next === 120 || next === 88 ) { return this . readRadixNumber ( 16 ) } // '0x', '0X' - hex number
if ( this . options . ecmaVersion >= 6 ) {
if ( next === 111 || next === 79 ) { return this . readRadixNumber ( 8 ) } // '0o', '0O' - octal number
if ( next === 98 || next === 66 ) { return this . readRadixNumber ( 2 ) } // '0b', '0B' - binary number
}
// Anything else beginning with a digit is an integer, octal
// number, or float.
case 49 : case 50 : case 51 : case 52 : case 53 : case 54 : case 55 : case 56 : case 57 : // 1-9
return this . readNumber ( false )
// Quotes produce strings.
case 34 : case 39 : // '"', "'"
return this . readString ( code )
// Operators are parsed inline in tiny state machines. '=' (61) is
// often referred to. `finishOp` simply skips the amount of
// characters it is given as second argument, and returns a token
// of the type given by its first argument.
case 47 : // '/'
return this . readToken _slash ( )
case 37 : case 42 : // '%*'
return this . readToken _mult _modulo _exp ( code )
case 124 : case 38 : // '|&'
return this . readToken _pipe _amp ( code )
case 94 : // '^'
return this . readToken _caret ( )
case 43 : case 45 : // '+-'
return this . readToken _plus _min ( code )
case 60 : case 62 : // '<>'
return this . readToken _lt _gt ( code )
case 61 : case 33 : // '=!'
return this . readToken _eq _excl ( code )
case 126 : // '~'
return this . finishOp ( types . prefix , 1 )
}
this . raise ( this . pos , "Unexpected character '" + codePointToString ( code ) + "'" ) ;
} ;
pp$8 . finishOp = function ( type , size ) {
var str = this . input . slice ( this . pos , this . pos + size ) ;
this . pos += size ;
return this . finishToken ( type , str )
} ;
// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.
function tryCreateRegexp ( src , flags , throwErrorAt , parser ) {
try {
return new RegExp ( src , flags )
} catch ( e ) {
if ( throwErrorAt !== undefined ) {
if ( e instanceof SyntaxError ) { parser . raise ( throwErrorAt , "Error parsing regular expression: " + e . message ) ; }
throw e
}
}
}
var regexpUnicodeSupport = ! ! tryCreateRegexp ( "\uffff" , "u" ) ;
pp$8 . readRegexp = function ( ) {
var this $1 = this ;
var escaped , inClass , start = this . pos ;
for ( ; ; ) {
if ( this $1 . pos >= this $1 . input . length ) { this $1 . raise ( start , "Unterminated regular expression" ) ; }
var ch = this $1 . input . charAt ( this $1 . pos ) ;
if ( lineBreak . test ( ch ) ) { this $1 . raise ( start , "Unterminated regular expression" ) ; }
if ( ! escaped ) {
if ( ch === "[" ) { inClass = true ; }
else if ( ch === "]" && inClass ) { inClass = false ; }
else if ( ch === "/" && ! inClass ) { break }
escaped = ch === "\\" ;
} else { escaped = false ; }
++ this $1 . pos ;
}
var content = this . input . slice ( start , this . pos ) ;
++ this . pos ;
var flagsStart = this . pos ;
var mods = this . readWord1 ( ) ;
if ( this . containsEsc ) { this . unexpected ( flagsStart ) ; }
var tmp = content , tmpFlags = "" ;
if ( mods ) {
var validFlags = "gim" ;
if ( this . options . ecmaVersion >= 6 ) { validFlags += "uy" ; }
if ( this . options . ecmaVersion >= 9 ) { validFlags += "s" ; }
for ( var i = 0 ; i < mods . length ; i ++ ) {
var mod = mods . charAt ( i ) ;
if ( validFlags . indexOf ( mod ) == - 1 ) { this $1 . raise ( start , "Invalid regular expression flag" ) ; }
if ( mods . indexOf ( mod , i + 1 ) > - 1 ) { this $1 . raise ( start , "Duplicate regular expression flag" ) ; }
}
if ( mods . indexOf ( "u" ) >= 0 ) {
if ( regexpUnicodeSupport ) {
tmpFlags = "u" ;
} else {
// Replace each astral symbol and every Unicode escape sequence that
// possibly represents an astral symbol or a paired surrogate with a
// single ASCII symbol to avoid throwing on regular expressions that
// are only valid in combination with the `/u` flag.
// Note: replacing with the ASCII symbol `x` might cause false
// negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
// perfectly valid pattern that is equivalent to `[a-b]`, but it would
// be replaced by `[x-b]` which throws an error.
tmp = tmp . replace ( /\\u\{([0-9a-fA-F]+)\}/g , function ( _match , code , offset ) {
code = Number ( "0x" + code ) ;
if ( code > 0x10FFFF ) { this $1 . raise ( start + offset + 3 , "Code point out of bounds" ) ; }
return "x"
} ) ;
tmp = tmp . replace ( /\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g , "x" ) ;
tmpFlags = tmpFlags . replace ( "u" , "" ) ;
}
}
}
// Detect invalid regular expressions.
var value = null ;
// Rhino's regular expression parser is flaky and throws uncatchable exceptions,
// so don't do detection if we are running under Rhino
if ( ! isRhino ) {
tryCreateRegexp ( tmp , tmpFlags , start , this ) ;
// Get a regular expression object for this pattern-flag pair, or `null` in
// case the current environment doesn't support the flags it uses.
value = tryCreateRegexp ( content , mods ) ;
}
return this . finishToken ( types . regexp , { pattern : content , flags : mods , value : value } )
} ;
// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.
pp$8 . readInt = function ( radix , len ) {
var this $1 = this ;
var start = this . pos , total = 0 ;
for ( var i = 0 , e = len == null ? Infinity : len ; i < e ; ++ i ) {
var code = this $1 . input . charCodeAt ( this $1 . pos ) , val = ( void 0 ) ;
if ( code >= 97 ) { val = code - 97 + 10 ; } // a
else if ( code >= 65 ) { val = code - 65 + 10 ; } // A
else if ( code >= 48 && code <= 57 ) { val = code - 48 ; } // 0-9
else { val = Infinity ; }
if ( val >= radix ) { break }
++ this $1 . pos ;
total = total * radix + val ;
}
if ( this . pos === start || len != null && this . pos - start !== len ) { return null }
return total
} ;
pp$8 . readRadixNumber = function ( radix ) {
this . pos += 2 ; // 0x
var val = this . readInt ( radix ) ;
if ( val == null ) { this . raise ( this . start + 2 , "Expected number in radix " + radix ) ; }
if ( isIdentifierStart ( this . fullCharCodeAtPos ( ) ) ) { this . raise ( this . pos , "Identifier directly after number" ) ; }
return this . finishToken ( types . num , val )
} ;
// Read an integer, octal integer, or floating-point number.
pp$8 . readNumber = function ( startsWithDot ) {
var start = this . pos ;
if ( ! startsWithDot && this . readInt ( 10 ) === null ) { this . raise ( start , "Invalid number" ) ; }
var octal = this . pos - start >= 2 && this . input . charCodeAt ( start ) === 48 ;
if ( octal && this . strict ) { this . raise ( start , "Invalid number" ) ; }
if ( octal && /[89]/ . test ( this . input . slice ( start , this . pos ) ) ) { octal = false ; }
var next = this . input . charCodeAt ( this . pos ) ;
if ( next === 46 && ! octal ) { // '.'
++ this . pos ;
this . readInt ( 10 ) ;
next = this . input . charCodeAt ( this . pos ) ;
}
if ( ( next === 69 || next === 101 ) && ! octal ) { // 'eE'
next = this . input . charCodeAt ( ++ this . pos ) ;
if ( next === 43 || next === 45 ) { ++ this . pos ; } // '+-'
if ( this . readInt ( 10 ) === null ) { this . raise ( start , "Invalid number" ) ; }
}
if ( isIdentifierStart ( this . fullCharCodeAtPos ( ) ) ) { this . raise ( this . pos , "Identifier directly after number" ) ; }
var str = this . input . slice ( start , this . pos ) ;
var val = octal ? parseInt ( str , 8 ) : parseFloat ( str ) ;
return this . finishToken ( types . num , val )
} ;
// Read a string value, interpreting backslash-escapes.
pp$8 . readCodePoint = function ( ) {
var ch = this . input . charCodeAt ( this . pos ) , code ;
if ( ch === 123 ) { // '{'
if ( this . options . ecmaVersion < 6 ) { this . unexpected ( ) ; }
var codePos = ++ this . pos ;
code = this . readHexChar ( this . input . indexOf ( "}" , this . pos ) - this . pos ) ;
++ this . pos ;
if ( code > 0x10FFFF ) { this . invalidStringToken ( codePos , "Code point out of bounds" ) ; }
} else {
code = this . readHexChar ( 4 ) ;
}
return code
} ;
function codePointToString ( code ) {
// UTF-16 Decoding
if ( code <= 0xFFFF ) { return String . fromCharCode ( code ) }
code -= 0x10000 ;
return String . fromCharCode ( ( code >> 10 ) + 0xD800 , ( code & 1023 ) + 0xDC00 )
}
pp$8 . readString = function ( quote ) {
var this $1 = this ;
var out = "" , chunkStart = ++ this . pos ;
for ( ; ; ) {
if ( this $1 . pos >= this $1 . input . length ) { this $1 . raise ( this $1 . start , "Unterminated string constant" ) ; }
var ch = this $1 . input . charCodeAt ( this $1 . pos ) ;
if ( ch === quote ) { break }
if ( ch === 92 ) { // '\'
out += this $1 . input . slice ( chunkStart , this $1 . pos ) ;
out += this $1 . readEscapedChar ( false ) ;
chunkStart = this $1 . pos ;
} else {
if ( isNewLine ( ch ) ) { this $1 . raise ( this $1 . start , "Unterminated string constant" ) ; }
++ this $1 . pos ;
}
}
out += this . input . slice ( chunkStart , this . pos ++ ) ;
return this . finishToken ( types . string , out )
} ;
// Reads template string tokens.
var INVALID _TEMPLATE _ESCAPE _ERROR = { } ;
pp$8 . tryReadTemplateToken = function ( ) {
this . inTemplateElement = true ;
try {
this . readTmplToken ( ) ;
} catch ( err ) {
if ( err === INVALID _TEMPLATE _ESCAPE _ERROR ) {
this . readInvalidTemplateToken ( ) ;
} else {
throw err
}
}
this . inTemplateElement = false ;
} ;
pp$8 . invalidStringToken = function ( position , message ) {
if ( this . inTemplateElement && this . options . ecmaVersion >= 9 ) {
throw INVALID _TEMPLATE _ESCAPE _ERROR
} else {
this . raise ( position , message ) ;
}
} ;
pp$8 . readTmplToken = function ( ) {
var this $1 = this ;
var out = "" , chunkStart = this . pos ;
for ( ; ; ) {
if ( this $1 . pos >= this $1 . input . length ) { this $1 . raise ( this $1 . start , "Unterminated template" ) ; }
var ch = this $1 . input . charCodeAt ( this $1 . pos ) ;
if ( ch === 96 || ch === 36 && this $1 . input . charCodeAt ( this $1 . pos + 1 ) === 123 ) { // '`', '${'
if ( this $1 . pos === this $1 . start && ( this $1 . type === types . template || this $1 . type === types . invalidTemplate ) ) {
if ( ch === 36 ) {
this $1 . pos += 2 ;
return this $1 . finishToken ( types . dollarBraceL )
} else {
++ this $1 . pos ;
return this $1 . finishToken ( types . backQuote )
}
}
out += this $1 . input . slice ( chunkStart , this $1 . pos ) ;
return this $1 . finishToken ( types . template , out )
}
if ( ch === 92 ) { // '\'
out += this $1 . input . slice ( chunkStart , this $1 . pos ) ;
out += this $1 . readEscapedChar ( true ) ;
chunkStart = this $1 . pos ;
} else if ( isNewLine ( ch ) ) {
out += this $1 . input . slice ( chunkStart , this $1 . pos ) ;
++ this $1 . pos ;
switch ( ch ) {
case 13 :
if ( this $1 . input . charCodeAt ( this $1 . pos ) === 10 ) { ++ this $1 . pos ; }
case 10 :
out += "\n" ;
break
default :
out += String . fromCharCode ( ch ) ;
break
}
if ( this $1 . options . locations ) {
++ this $1 . curLine ;
this $1 . lineStart = this $1 . pos ;
}
chunkStart = this $1 . pos ;
} else {
++ this $1 . pos ;
}
}
} ;
// Reads a template token to search for the end, without validating any escape sequences
pp$8 . readInvalidTemplateToken = function ( ) {
var this $1 = this ;
for ( ; this . pos < this . input . length ; this . pos ++ ) {
switch ( this $1 . input [ this $1 . pos ] ) {
case "\\" :
++ this $1 . pos ;
break
case "$" :
if ( this $1 . input [ this $1 . pos + 1 ] !== "{" ) {
break
}
// falls through
case "`" :
return this $1 . finishToken ( types . invalidTemplate , this $1 . input . slice ( this $1 . start , this $1 . pos ) )
// no default
}
}
this . raise ( this . start , "Unterminated template" ) ;
} ;
// Used to read escaped characters
pp$8 . readEscapedChar = function ( inTemplate ) {
var ch = this . input . charCodeAt ( ++ this . pos ) ;
++ this . pos ;
switch ( ch ) {
case 110 : return "\n" // 'n' -> '\n'
case 114 : return "\r" // 'r' -> '\r'
case 120 : return String . fromCharCode ( this . readHexChar ( 2 ) ) // 'x'
case 117 : return codePointToString ( this . readCodePoint ( ) ) // 'u'
case 116 : return "\t" // 't' -> '\t'
case 98 : return "\b" // 'b' -> '\b'
case 118 : return "\u000b" // 'v' -> '\u000b'
case 102 : return "\f" // 'f' -> '\f'
case 13 : if ( this . input . charCodeAt ( this . pos ) === 10 ) { ++ this . pos ; } // '\r\n'
case 10 : // ' \n'
if ( this . options . locations ) { this . lineStart = this . pos ; ++ this . curLine ; }
return ""
default :
if ( ch >= 48 && ch <= 55 ) {
var octalStr = this . input . substr ( this . pos - 1 , 3 ) . match ( /^[0-7]+/ ) [ 0 ] ;
var octal = parseInt ( octalStr , 8 ) ;
if ( octal > 255 ) {
octalStr = octalStr . slice ( 0 , - 1 ) ;
octal = parseInt ( octalStr , 8 ) ;
}
this . pos += octalStr . length - 1 ;
ch = this . input . charCodeAt ( this . pos ) ;
if ( ( octalStr !== "0" || ch == 56 || ch == 57 ) && ( this . strict || inTemplate ) ) {
this . invalidStringToken ( this . pos - 1 - octalStr . length , "Octal literal in strict mode" ) ;
}
return String . fromCharCode ( octal )
}
return String . fromCharCode ( ch )
}
} ;
// Used to read character escape sequences ('\x', '\u', '\U').
pp$8 . readHexChar = function ( len ) {
var codePos = this . pos ;
var n = this . readInt ( 16 , len ) ;
if ( n === null ) { this . invalidStringToken ( codePos , "Bad character escape sequence" ) ; }
return n
} ;
// Read an identifier, and return it as a string. Sets `this.containsEsc`
// to whether the word contained a '\u' escape.
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.
pp$8 . readWord1 = function ( ) {
var this $1 = this ;
this . containsEsc = false ;
var word = "" , first = true , chunkStart = this . pos ;
var astral = this . options . ecmaVersion >= 6 ;
while ( this . pos < this . input . length ) {
var ch = this $1 . fullCharCodeAtPos ( ) ;
if ( isIdentifierChar ( ch , astral ) ) {
this $1 . pos += ch <= 0xffff ? 1 : 2 ;
} else if ( ch === 92 ) { // "\"
this $1 . containsEsc = true ;
word += this $1 . input . slice ( chunkStart , this $1 . pos ) ;
var escStart = this $1 . pos ;
if ( this $1 . input . charCodeAt ( ++ this $1 . pos ) != 117 ) // "u"
{ this $1 . invalidStringToken ( this $1 . pos , "Expecting Unicode escape sequence \\uXXXX" ) ; }
++ this $1 . pos ;
var esc = this $1 . readCodePoint ( ) ;
if ( ! ( first ? isIdentifierStart : isIdentifierChar ) ( esc , astral ) )
{ this $1 . invalidStringToken ( escStart , "Invalid Unicode escape" ) ; }
word += codePointToString ( esc ) ;
chunkStart = this $1 . pos ;
} else {
break
}
first = false ;
}
return word + this . input . slice ( chunkStart , this . pos )
} ;
// Read an identifier or keyword token. Will check for reserved
// words when necessary.
pp$8 . readWord = function ( ) {
var word = this . readWord1 ( ) ;
var type = types . name ;
if ( this . keywords . test ( word ) ) {
if ( this . containsEsc ) { this . raiseRecoverable ( this . start , "Escape sequence in keyword " + word ) ; }
type = keywords$1 [ word ] ;
}
return this . finishToken ( type , word )
} ;
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
// various contributors and released under an MIT license.
//
// Git repositories for Acorn are available at
//
// http://marijnhaverbeke.nl/git/acorn
// https://github.com/acornjs/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/acornjs/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
var version = "5.4.1" ;
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
// returns an abstract syntax tree as specified by [Mozilla parser
// API][api].
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
function parse ( input , options ) {
return new Parser ( options , input ) . parse ( )
}
// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.
function parseExpressionAt ( input , pos , options ) {
var p = new Parser ( options , input , pos ) ;
p . nextToken ( ) ;
return p . parseExpression ( )
}
// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenizer` export provides an interface to the tokenizer.
function tokenizer ( input , options ) {
return new Parser ( options , input )
}
// This is a terrible kludge to support the existing, pre-ES6
// interface where the loose parser module retroactively adds exports
// to this module.
// eslint-disable-line camelcase
function addLooseExports ( parse , Parser$$1 , plugins$$1 ) {
exports . parse _dammit = parse ; // eslint-disable-line camelcase
exports . LooseParser = Parser$$1 ;
exports . pluginsLoose = plugins$$1 ;
}
exports . version = version ;
exports . parse = parse ;
exports . parseExpressionAt = parseExpressionAt ;
exports . tokenizer = tokenizer ;
exports . addLooseExports = addLooseExports ;
exports . Parser = Parser ;
exports . plugins = plugins ;
exports . defaultOptions = defaultOptions ;
exports . Position = Position ;
exports . SourceLocation = SourceLocation ;
exports . getLineInfo = getLineInfo ;
exports . Node = Node ;
exports . TokenType = TokenType ;
exports . tokTypes = types ;
exports . keywordTypes = keywords$1 ;
exports . TokContext = TokContext ;
exports . tokContexts = types$1 ;
exports . isIdentifierChar = isIdentifierChar ;
exports . isIdentifierStart = isIdentifierStart ;
exports . Token = Token ;
exports . isNewLine = isNewLine ;
exports . lineBreak = lineBreak ;
exports . lineBreakG = lineBreakG ;
exports . nonASCIIwhitespace = nonASCIIwhitespace ;
Object . defineProperty ( exports , '__esModule' , { value : true } ) ;
} ) ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Utilities
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
function init ( ) {
}
function isWindows ( ) {
return ( typeof navigator != "undefined" ) && navigator . userAgent . indexOf ( "Windows" ) >= 0 ;
}
function isAppleDevice ( ) {
return ( typeof navigator != "undefined" ) && ( typeof window != "undefined" ) && /iPad|iPhone|iPod/ . test ( navigator . userAgent ) && ! window . MSStream ;
}
function getChromeVersion ( ) {
return parseInt ( window . navigator . appVersion . match ( /Chrome\/(.*?) / ) [ 1 ] . split ( "." ) [ 0 ] ) ;
}
function isNWApp ( ) {
return ( typeof require === "function" ) && ( typeof require ( 'nw.gui' ) !== "undefined" ) ;
}
function isChromeWebApp ( ) {
return ( ( typeof chrome === "object" ) && chrome . app && chrome . app . window ) ;
}
function isProgressiveWebApp ( ) {
return ! isNWApp ( ) && ! isChromeWebApp ( ) && window && window . matchMedia && window . matchMedia ( '(display-mode: standalone)' ) . matches ;
}
function hasNativeTitleBar ( ) {
return ! isNWApp ( ) && ! isChromeWebApp ( ) ;
}
function escapeHTML ( text , escapeSpaces )
{
escapeSpaces = typeof escapeSpaces !== 'undefined' ? escapeSpaces : true ;
var chr = { '"' : '"' , '&' : '&' , '<' : '<' , '>' : '>' , ' ' : ( escapeSpaces ? ' ' : ' ' ) } ;
return text . toString ( ) . replace ( /[\"&<> ]/g , function ( a ) { return chr [ a ] ; } ) ;
}
/ * G o o g l e D o c s , f o r u m s , e t c t e n d t o b r e a k c o d e b y r e p l a c i n g c h a r a c t e r s w i t h
fancy unicode versions . Un - break the code by undoing these changes * /
function fixBrokenCode ( text )
{
// make sure we ignore `­` - which gets inserted
// by the forum's code formatter
text = text . replace ( /\u00AD/g , '' ) ;
// replace quotes that get auto-replaced by Google Docs and other editors
text = text . replace ( /[\u201c\u201d]/g , '"' ) ;
text = text . replace ( /[\u2018\u2019]/g , '\'' ) ;
return text ;
}
function getSubString ( str , from , len ) {
if ( len == undefined ) {
return str . substr ( from , len ) ;
} else {
var s = str . substr ( from , len ) ;
while ( s . length < len ) s += " " ;
return s ;
}
} ;
/ * * G e t a L e x e r t o p a r s e J a v a S c r i p t - t h i s i s r e a l l y v e r y n a s t y r i g h t n o w a n d i t d o e s n ' t l e x e v e n r e m o t e l y p r o p e r l y .
* It ' ll return { type : "type" , str : "chars that were parsed" , value : "string" , startIdx : Index in string of the start , endIdx : Index in string of the end } , until EOF when it returns undefined * /
function getLexer ( str ) {
// Nasty lexer - no comments/etc
var chAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$" ;
var chNum = "0123456789" ;
var chAlphaNum = chAlpha + chNum ;
var chWhiteSpace = " \t\n\r" ;
var chQuotes = "\"'`" ;
var ch ;
var idx = 0 ;
var lineNumber = 1 ;
var nextCh = function ( ) {
ch = str [ idx ++ ] ;
if ( ch == "\n" ) lineNumber ++ ;
} ;
nextCh ( ) ;
var isIn = function ( s , c ) { return s . indexOf ( c ) >= 0 ; } ;
var nextToken = function ( ) {
while ( isIn ( chWhiteSpace , ch ) ) {
nextCh ( ) ;
}
if ( ch == undefined ) return undefined ;
if ( ch == "/" ) {
nextCh ( ) ;
if ( ch == "/" ) {
// single line comment
while ( ch !== undefined && ch != "\n" ) nextCh ( ) ;
return nextToken ( ) ;
} else if ( ch == "*" ) {
nextCh ( ) ;
var last = ch ;
nextCh ( ) ;
// multiline comment
while ( ch !== undefined && ! ( last == "*" && ch == "/" ) ) {
last = ch ;
nextCh ( ) ;
}
nextCh ( ) ;
return nextToken ( ) ;
}
return { type : "CHAR" , str : "/" , value : "/" , startIdx : idx - 2 , endIdx : idx - 1 , lineNumber : lineNumber } ;
}
var s = "" ;
var type , value ;
var startIdx = idx - 1 ;
if ( isIn ( chAlpha , ch ) ) { // ID
type = "ID" ;
do {
s += ch ;
nextCh ( ) ;
} while ( isIn ( chAlphaNum , ch ) ) ;
} else if ( isIn ( chNum , ch ) ) { // NUMBER
type = "NUMBER" ;
2020-04-27 16:00:54 +00:00
var chRange = chNum ;
if ( ch == "0" ) { // Handle
s += ch ;
nextCh ( ) ;
if ( "xXoObB" . indexOf ( ch ) >= 0 ) {
if ( ch == "b" || ch == "B" ) chRange = "01" ;
if ( ch == "o" || ch == "O" ) chRange = "01234567" ;
if ( ch == "x" || ch == "X" ) chRange = "0123456789ABCDEFabcdef" ;
s += ch ;
nextCh ( ) ;
}
}
while ( isIn ( chRange , ch ) || ch == "." ) {
2020-04-27 15:12:13 +00:00
s += ch ;
nextCh ( ) ;
2020-04-27 16:00:54 +00:00
}
2020-04-27 15:12:13 +00:00
} else if ( isIn ( chQuotes , ch ) ) { // STRING
type = "STRING" ;
var q = ch ;
value = "" ;
s += ch ;
nextCh ( ) ;
while ( ch !== undefined && ch != q ) {
s += ch ;
if ( ch == "\\" ) {
nextCh ( ) ;
s += ch ;
// FIXME: handle hex/etc correctly here
}
value += ch ;
nextCh ( ) ;
} ;
if ( ch !== undefined ) s += ch ;
nextCh ( ) ;
} else {
type = "CHAR" ;
s += ch ;
nextCh ( ) ;
}
if ( value === undefined ) value = s ;
return { type : type , str : s , value : value , startIdx : startIdx , endIdx : idx - 1 , lineNumber : lineNumber } ;
} ;
return {
next : nextToken
} ;
} ;
/** Count brackets in a string - will be 0 if all are closed */
function countBrackets ( str ) {
var lex = getLexer ( str ) ;
var brackets = 0 ;
var tok = lex . next ( ) ;
while ( tok !== undefined ) {
if ( tok . str == "(" || tok . str == "{" || tok . str == "[" ) brackets ++ ;
if ( tok . str == ")" || tok . str == "}" || tok . str == "]" ) brackets -- ;
tok = lex . next ( ) ;
}
return brackets ;
}
/ * * T r y a n d g e t a p r o m p t f r o m E s p r u i n o - i f w e d o n ' t s e e o n e , i s s u e C t r l - C
* and hope it comes back . Calls callback with first argument true if it
had to Ctrl - C out * /
function getEspruinoPrompt ( callback ) {
if ( Espruino . Core . Terminal !== undefined &&
Espruino . Core . Terminal . getTerminalLine ( ) == ">" ) {
console . log ( "Found a prompt... great!" ) ;
return callback ( ) ;
}
var receivedData = "" ;
var prevReader = Espruino . Core . Serial . startListening ( function ( readData ) {
var bufView = new Uint8Array ( readData ) ;
for ( var i = 0 ; i < bufView . length ; i ++ ) {
receivedData += String . fromCharCode ( bufView [ i ] ) ;
}
if ( receivedData [ receivedData . length - 1 ] == ">" ) {
if ( receivedData . substr ( - 6 ) == "debug>" ) {
console . log ( "Got debug> - sending Ctrl-C to break out and we'll be good" ) ;
Espruino . Core . Serial . write ( '\x03' ) ;
} else {
if ( receivedData == "\r\n=undefined\r\n>" )
receivedData = "" ; // this was just what we expected - so ignore it
console . log ( "Received a prompt after sending newline... good!" ) ;
clearTimeout ( timeout ) ;
nextStep ( ) ;
}
}
} ) ;
// timeout in case something goes wrong...
var hadToBreak = false ;
var timeout = setTimeout ( function ( ) {
console . log ( "Got " + JSON . stringify ( receivedData ) ) ;
// if we haven't had the prompt displayed for us, Ctrl-C to break out of what we had
console . log ( "No Prompt found, got " + JSON . stringify ( receivedData [ receivedData . length - 1 ] ) + " - issuing Ctrl-C to try and break out" ) ;
Espruino . Core . Serial . write ( '\x03' ) ;
hadToBreak = true ;
timeout = setTimeout ( function ( ) {
console . log ( "Still no prompt - issuing another Ctrl-C" ) ;
Espruino . Core . Serial . write ( '\x03' ) ;
nextStep ( ) ;
} , 500 ) ;
} , 500 ) ;
// when we're done...
var nextStep = function ( ) {
// send data to console anyway...
if ( prevReader ) prevReader ( receivedData ) ;
receivedData = "" ;
// start the previous reader listening again
Espruino . Core . Serial . startListening ( prevReader ) ;
// call our callback
if ( callback ) callback ( hadToBreak ) ;
} ;
// send a newline, and we hope we'll see '=undefined\r\n>'
Espruino . Core . Serial . write ( '\n' ) ;
} ;
/ * * R e t u r n t h e v a l u e o f e x e c u t i n g a n e x p r e s s i o n o n t h e b o a r d . I f
If exprPrintsResult = false / undefined the actual value returned by the expression is returned .
If exprPrintsResult = true , whatever expression prints to the console is returned * /
function executeExpression ( expressionToExecute , callback , exprPrintsResult ) {
var receivedData = "" ;
var hadDataSinceTimeout = false ;
var allDataSent = false ;
var progress = 100 ;
function incrementProgress ( ) {
if ( progress == 100 ) {
Espruino . Core . Status . setStatus ( "Receiving..." , 100 ) ;
progress = 0 ;
} else {
progress ++ ;
Espruino . Core . Status . incrementProgress ( 1 ) ;
}
}
function getProcessInfo ( expressionToExecute , callback ) {
var prevReader = Espruino . Core . Serial . startListening ( function ( readData ) {
var bufView = new Uint8Array ( readData ) ;
for ( var i = 0 ; i < bufView . length ; i ++ ) {
receivedData += String . fromCharCode ( bufView [ i ] ) ;
}
if ( allDataSent ) incrementProgress ( ) ;
// check if we got what we wanted
var startProcess = receivedData . indexOf ( "< <<" ) ;
var endProcess = receivedData . indexOf ( ">> >" , startProcess ) ;
if ( startProcess >= 0 && endProcess > 0 ) {
// All good - get the data!
var result = receivedData . substring ( startProcess + 4 , endProcess ) ;
console . log ( "Got " + JSON . stringify ( receivedData ) ) ;
// strip out the text we found
receivedData = receivedData . substr ( 0 , startProcess ) + receivedData . substr ( endProcess + 4 ) ;
// Now stop time timeout
if ( timeout ) clearInterval ( timeout ) ;
timeout = "cancelled" ;
// Do the next stuff
nextStep ( result ) ;
} else if ( startProcess >= 0 ) {
// we got some data - so keep waiting...
hadDataSinceTimeout = true ;
}
} ) ;
// when we're done...
var nextStep = function ( result ) {
Espruino . Core . Status . setStatus ( "" ) ;
// start the previous reader listing again
Espruino . Core . Serial . startListening ( prevReader ) ;
// forward the original text to the previous reader
if ( prevReader ) prevReader ( receivedData ) ;
// run the callback
callback ( result ) ;
} ;
var timeout = undefined ;
// Don't Ctrl-C, as we've already got ourselves a prompt with Espruino.Core.Utils.getEspruinoPrompt
var cmd ;
if ( exprPrintsResult )
cmd = '\x10print("<","<<");' + expressionToExecute + ';print(">>",">")\n' ;
else
cmd = '\x10print("<","<<",JSON.stringify(' + expressionToExecute + '),">>",">")\n' ;
Espruino . Core . Serial . write ( cmd ,
undefined , function ( ) {
allDataSent = true ;
// now it's sent, wait for data
var maxTimeout = 30 ; // seconds - how long we wait if we're getting data
var minTimeout = 2 ; // seconds - how long we wait if we're not getting data
var pollInterval = 500 ; // milliseconds
var timeoutSeconds = 0 ;
if ( timeout != "cancelled" ) {
timeout = setInterval ( function onTimeout ( ) {
incrementProgress ( ) ;
timeoutSeconds += pollInterval / 1000 ;
// if we're still getting data, keep waiting for up to 10 secs
if ( hadDataSinceTimeout && timeoutSeconds < maxTimeout ) {
hadDataSinceTimeout = false ;
} else if ( timeoutSeconds > minTimeout ) {
// No data yet...
// OR we keep getting data for > maxTimeout seconds
clearInterval ( timeout ) ;
console . warn ( "No result found for " + JSON . stringify ( expressionToExecute ) + " - just got " + JSON . stringify ( receivedData ) ) ;
nextStep ( undefined ) ;
}
} , pollInterval ) ;
}
} ) ;
}
if ( Espruino . Core . Serial . isConnected ( ) ) {
Espruino . Core . Utils . getEspruinoPrompt ( function ( ) {
getProcessInfo ( expressionToExecute , callback ) ;
} ) ;
} else {
console . error ( "executeExpression called when not connected!" ) ;
callback ( undefined ) ;
}
} ;
function versionToFloat ( version ) {
return parseFloat ( version . trim ( ) . replace ( "v" , "." ) ) ;
} ;
/// Gets a URL, and returns callback(data) or callback(undefined) on error
function getURL ( url , callback ) {
Espruino . callProcessor ( "getURL" , { url : url , data : undefined } , function ( result ) {
if ( result . data !== undefined ) {
callback ( result . data ) ;
} else {
var resultUrl = result . url ? result . url : url ;
if ( typeof process === 'undefined' ) {
// Web browser
var xhr = new XMLHttpRequest ( ) ;
xhr . responseType = "text" ;
xhr . addEventListener ( "load" , function ( ) {
if ( xhr . status === 200 ) {
callback ( xhr . response . toString ( ) ) ;
} else {
console . error ( "getURL(" + JSON . stringify ( url ) + ") error : HTTP " + xhr . status ) ;
callback ( undefined ) ;
}
} ) ;
xhr . addEventListener ( "error" , function ( e ) {
console . error ( "getURL(" + JSON . stringify ( url ) + ") error " + e ) ;
callback ( undefined ) ;
} ) ;
xhr . open ( "GET" , url , true ) ;
xhr . send ( null ) ;
} else {
// Node.js
if ( resultUrl . substr ( 0 , 4 ) == "http" ) {
var m = resultUrl [ 4 ] == "s" ? "https" : "http" ;
var http _options = Espruino . Config . MODULE _PROXY _ENABLED ? {
host : Espruino . Config . MODULE _PROXY _URL ,
port : Espruino . Config . MODULE _PROXY _PORT ,
path : resultUrl ,
} : resultUrl ;
require ( m ) . get ( http _options , function ( res ) {
if ( res . statusCode != 200 ) {
console . log ( "Espruino.Core.Utils.getURL: got HTTP status code " + res . statusCode + " for " + url ) ;
return callback ( undefined ) ;
}
var data = "" ;
res . on ( "data" , function ( d ) { data += d ; } ) ;
res . on ( "end" , function ( ) {
callback ( data ) ;
} ) ;
} ) . on ( 'error' , function ( err ) {
console . error ( "getURL(" + JSON . stringify ( url ) + ") error : " + err ) ;
callback ( undefined ) ;
} ) ;
} else {
require ( "fs" ) . readFile ( resultUrl , function ( err , d ) {
if ( err ) {
console . error ( err ) ;
callback ( undefined ) ;
} else
callback ( d . toString ( ) ) ;
} ) ;
}
}
}
} ) ;
}
/// Gets a URL as a Binary file, returning callback(err, ArrayBuffer)
var getBinaryURL = function ( url , callback ) {
console . log ( "Downloading " + url ) ;
Espruino . Core . Status . setStatus ( "Downloading binary..." ) ;
var xhr = new XMLHttpRequest ( ) ;
xhr . responseType = "arraybuffer" ;
xhr . addEventListener ( "load" , function ( ) {
if ( xhr . status === 200 ) {
Espruino . Core . Status . setStatus ( "Done." ) ;
var data = xhr . response ;
callback ( undefined , data ) ;
} else
callback ( "Error downloading file - HTTP " + xhr . status ) ;
} ) ;
xhr . addEventListener ( "error" , function ( ) {
callback ( "Error downloading file" ) ;
} ) ;
xhr . open ( "GET" , url , true ) ;
xhr . send ( null ) ;
} ;
/// Gets a URL as JSON, and returns callback(data) or callback(undefined) on error
function getJSONURL ( url , callback ) {
getURL ( url , function ( d ) {
if ( ! d ) return callback ( d ) ;
var j ;
try { j = JSON . parse ( d ) ; } catch ( e ) { console . error ( "Unable to parse JSON" , d ) ; }
callback ( j ) ;
} ) ;
}
function isURL ( text ) {
return ( new RegExp ( '(http|https)://' ) ) . test ( text ) ;
}
/ * A r e w e s e r v e d f r o m a s e c u r e l o c a t i o n s o w e ' r e
forced to use a secure get ? * /
function needsHTTPS ( ) {
if ( typeof window === "undefined" || ! window . location ) return false ;
return window . location . protocol == "https:" ;
}
/ * O p e n a f i l e l o a d d i a l o g .
options = {
id : ID is to ensure that subsequent calls with the same ID remember the last used directory .
type :
type == "text" => ( default ) Callback is called with a string
type == "arraybuffer" => Callback is called with an arraybuffer
mimeType : ( optional ) comma - separated list of accepted mime types for files or extensions ( eg . ".js,application/javascript" )
callback ( contents , mimeType , fileName )
* /
function fileOpenDialog ( options , callback ) {
options = options || { } ;
options . type = options . type || "text" ;
options . id = options . id || "default" ;
var loaderId = options . id + "FileLoader" ;
var fileLoader = document . getElementById ( loaderId ) ;
if ( ! fileLoader ) {
fileLoader = document . createElement ( "input" ) ;
fileLoader . setAttribute ( "id" , loaderId ) ;
fileLoader . setAttribute ( "type" , "file" ) ;
fileLoader . setAttribute ( "style" , "z-index:-2000;position:absolute;top:0px;left:0px;" ) ;
if ( options . mimeType )
fileLoader . setAttribute ( "accept" , options . mimeType ) ;
fileLoader . addEventListener ( 'click' , function ( e ) {
e . target . value = '' ; // handle repeated upload of the same file
} ) ;
fileLoader . addEventListener ( 'change' , function ( e ) {
if ( ! fileLoader . callback ) return ;
var files = e . target . files ;
var file = files [ 0 ] ;
var reader = new FileReader ( ) ;
reader . onload = function ( e ) {
/ * D o i n g r e a d e r . r e a d A s T e x t ( f i l e ) i n t e r p r e t s t h e f i l e a s U T F 8
which we don ' t want . * /
var result ;
if ( options . type == "text" ) {
var a = new Uint8Array ( e . target . result ) ;
result = "" ;
for ( var i = 0 ; i < a . length ; i ++ )
result += String . fromCharCode ( a [ i ] ) ;
} else
result = e . target . result ;
fileLoader . callback ( result , file . type , file . name ) ;
fileLoader . callback = undefined ;
} ;
if ( options . type == "text" || options . type == "arraybuffer" ) reader . readAsArrayBuffer ( file ) ;
else throw new Error ( "fileOpenDialog: unknown type " + options . type ) ;
} , false ) ;
document . body . appendChild ( fileLoader ) ;
}
fileLoader . callback = callback ;
fileLoader . click ( ) ;
}
2020-04-27 16:00:54 +00:00
/* Save a file with a save file dialog. callback(savedFileName) only called in chrome app case when we knopw the filename*/
function fileSaveDialog ( data , filename , callback ) {
2020-04-27 15:12:13 +00:00
function errorHandler ( ) {
Espruino . Core . Notifications . error ( "Error Saving" , true ) ;
}
if ( chrome . fileSystem ) {
// Chrome Web App / NW.js
chrome . fileSystem . chooseEntry ( { type : 'saveFile' , suggestedName : filename } , function ( writableFileEntry ) {
if ( ! writableFileEntry ) return ; // cancelled
writableFileEntry . createWriter ( function ( writer ) {
var blob = new Blob ( [ data ] , { type : "text/plain" } ) ;
writer . onerror = errorHandler ;
// when truncation has finished, write
writer . onwriteend = function ( e ) {
writer . onwriteend = function ( e ) {
console . log ( 'FileWriter: complete' ) ;
2020-04-27 16:00:54 +00:00
if ( callback ) callback ( writableFileEntry . name ) ;
2020-04-27 15:12:13 +00:00
} ;
console . log ( 'FileWriter: writing' ) ;
writer . write ( blob ) ;
} ;
// truncate
console . log ( 'FileWriter: truncating' ) ;
writer . truncate ( blob . size ) ;
} , errorHandler ) ;
} ) ;
} else {
var a = document . createElement ( "a" ) ,
file = new Blob ( [ data ] , { type : "text/plain" } ) ;
var url = URL . createObjectURL ( file ) ;
a . href = url ;
a . download = filename ;
document . body . appendChild ( a ) ;
a . click ( ) ;
setTimeout ( function ( ) {
document . body . removeChild ( a ) ;
window . URL . revokeObjectURL ( url ) ;
} , 0 ) ;
}
} ;
/** Bluetooth device names that we KNOW run Espruino */
function recognisedBluetoothDevices ( ) {
return [
"Puck.js" , "Pixl.js" , "MDBT42Q" , "Espruino" , "Badge" , "Thingy" , "RuuviTag" , "iTracker" , "Smartibot" , "Bangle.js" ,
] ;
}
/ * * I f w e c a n ' t f i n d s e r v i c e i n f o , a d d d e v i c e s
based only on their name * /
function isRecognisedBluetoothDevice ( name ) {
if ( ! name ) return false ;
var devs = recognisedBluetoothDevices ( ) ;
for ( var i = 0 ; i < devs . length ; i ++ )
if ( name . substr ( 0 , devs [ i ] . length ) == devs [ i ] )
return true ;
return false ;
}
function getVersion ( callback ) {
var xmlhttp = new XMLHttpRequest ( ) ;
var path = ( window . location . pathname . indexOf ( "relay" ) >= 0 ) ? "../" : "" ;
xmlhttp . open ( 'GET' , path + 'manifest.json' ) ;
xmlhttp . onload = function ( e ) {
var manifest = JSON . parse ( xmlhttp . responseText ) ;
callback ( manifest . version ) ;
} ;
xmlhttp . send ( null ) ;
}
function getVersionInfo ( callback ) {
getVersion ( function ( version ) {
var platform = "Web App" ;
if ( isNWApp ( ) )
platform = "NW.js Native App" ;
if ( isChromeWebApp ( ) )
platform = "Chrome App" ;
callback ( platform + ", v" + version ) ;
} ) ;
}
// Converts a string to an ArrayBuffer
function stringToArrayBuffer ( str ) {
var buf = new Uint8Array ( str . length ) ;
for ( var i = 0 ; i < str . length ; i ++ ) {
var ch = str . charCodeAt ( i ) ;
if ( ch >= 256 ) {
console . warn ( "stringToArrayBuffer got non-8 bit character - code " + ch ) ;
ch = "?" . charCodeAt ( 0 ) ;
}
buf [ i ] = ch ;
}
return buf . buffer ;
} ;
// Converts a string to a Buffer
function stringToBuffer ( str ) {
var buf = Buffer . alloc ( str . length ) ;
for ( var i = 0 ; i < buf . length ; i ++ ) {
buf . writeUInt8 ( str . charCodeAt ( i ) , i ) ;
}
return buf ;
} ;
// Converts a DataView to an ArrayBuffer
function dataViewToArrayBuffer ( str ) {
var bufView = new Uint8Array ( dv . byteLength ) ;
for ( var i = 0 ; i < bufView . length ; i ++ ) {
bufView [ i ] = dv . getUint8 ( i ) ;
}
return bufView . buffer ;
} ;
// Converts an ArrayBuffer to a string
function arrayBufferToString ( str ) {
return String . fromCharCode . apply ( null , new Uint8Array ( buf ) ) ;
} ;
/ * P a r s e s a J S O N s t r i n g i n t o J S , t a k i n g i n t o a c c o u n t s o m e o f t h e i s s u e s
with Espruino ' s JSON from 2 v04 and before * /
function parseJSONish ( str ) {
var lex = getLexer ( str ) ;
var tok = lex . next ( ) ;
var final = "" ;
while ( tok !== undefined ) {
var s = tok . str ;
if ( tok . type == "STRING" ) {
s = s . replace ( /\\([0-9])/g , "\\u000$1" ) ;
s = s . replace ( /\\x(..)/g , "\\u00$1" ) ;
}
final += s ;
tok = lex . next ( ) ;
}
return JSON . parse ( final ) ;
} ;
// Does the given string contain only ASCII characters?
function isASCII ( str ) {
for ( var i = 0 ; i < str . length ; i ++ ) {
var c = str . charCodeAt ( i ) ;
if ( ( c < 32 || c > 126 ) &&
( c != 10 ) && ( c != 13 ) && ( c != 9 ) ) return false ;
}
return true ;
}
// btoa that works on utf8
function btoa ( input ) {
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
var out = "" ;
var i = 0 ;
while ( i < input . length ) {
var octet _a = 0 | input . charCodeAt ( i ++ ) ;
var octet _b = 0 ;
var octet _c = 0 ;
var padding = 0 ;
if ( i < input . length ) {
octet _b = 0 | input . charCodeAt ( i ++ ) ;
if ( i < input . length ) {
octet _c = 0 | input . charCodeAt ( i ++ ) ;
padding = 0 ;
} else
padding = 1 ;
} else
padding = 2 ;
var triple = ( octet _a << 0x10 ) + ( octet _b << 0x08 ) + octet _c ;
out += b64 [ ( triple >> 18 ) & 63 ] +
b64 [ ( triple >> 12 ) & 63 ] +
( ( padding > 1 ) ? '=' : b64 [ ( triple >> 6 ) & 63 ] ) +
( ( padding > 0 ) ? '=' : b64 [ triple & 63 ] ) ;
}
return out ;
}
Espruino . Core . Utils = {
init : init ,
isWindows : isWindows ,
isAppleDevice : isAppleDevice ,
getChromeVersion : getChromeVersion ,
isNWApp : isNWApp ,
isChromeWebApp : isChromeWebApp ,
isProgressiveWebApp : isProgressiveWebApp ,
hasNativeTitleBar : hasNativeTitleBar ,
escapeHTML : escapeHTML ,
fixBrokenCode : fixBrokenCode ,
getSubString : getSubString ,
getLexer : getLexer ,
countBrackets : countBrackets ,
getEspruinoPrompt : getEspruinoPrompt ,
executeExpression : function ( expr , callback ) { executeExpression ( expr , callback , false ) ; } ,
executeStatement : function ( statement , callback ) { executeExpression ( statement , callback , true ) ; } ,
versionToFloat : versionToFloat ,
getURL : getURL ,
getBinaryURL : getBinaryURL ,
getJSONURL : getJSONURL ,
isURL : isURL ,
needsHTTPS : needsHTTPS ,
fileOpenDialog : fileOpenDialog ,
fileSaveDialog : fileSaveDialog ,
recognisedBluetoothDevices : recognisedBluetoothDevices ,
isRecognisedBluetoothDevice : isRecognisedBluetoothDevice ,
getVersion : getVersion ,
getVersionInfo : getVersionInfo ,
stringToArrayBuffer : stringToArrayBuffer ,
stringToBuffer : stringToBuffer ,
dataViewToArrayBuffer : dataViewToArrayBuffer ,
arrayBufferToString : arrayBufferToString ,
parseJSONish : parseJSONish ,
isASCII : isASCII ,
btoa : btoa
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Central place to store and retrieve Options
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
To use this , on your plugin ' s ` init ` function , do something like the
following :
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
Espruino . Core . Config . add ( "MAX_FOOBARS" , {
section : "Communications" , // Heading this will come under in the config screen
2020-04-27 16:00:54 +00:00
name : "Foobars" , // Nice name
2020-04-27 15:12:13 +00:00
description : "How many foobars?" , // More detail about this
type : "int" / "boolean" / "string" / { value1 : niceName , value2 : niceName } ,
2020-04-27 16:00:54 +00:00
defaultValue : 20 ,
2020-04-27 15:12:13 +00:00
onChange : function ( newValue ) { ... }
} ) ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
* onChange will be called whenever the value changes from the default
( including when it is loaded )
2020-04-27 16:00:54 +00:00
Then use :
2020-04-27 15:12:13 +00:00
Espruino . Config . MAX _FOOBARS in your code
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
/** See addSection and getSections */
var builtinSections = { } ;
function _get ( callback ) {
if ( typeof chrome !== 'undefined' && chrome . storage ) {
2020-04-27 16:00:54 +00:00
chrome . storage . sync . get ( "CONFIGS" , function ( data ) {
2020-04-27 15:12:13 +00:00
var value = data [ "CONFIGS" ] ;
console . log ( "GET chrome.storage.sync = " + JSON . stringify ( value ) ) ;
callback ( value ) ;
} ) ;
} else if ( typeof document != "undefined" ) {
var data = { } ;
var cookie = document . cookie ;
if ( cookie !== undefined && cookie . indexOf ( "CONFIG=" ) >= 0 ) {
cookie = cookie . substring ( cookie . indexOf ( "CONFIG=" ) + 7 ) ;
cookie = cookie . substring ( 0 , cookie . indexOf ( ";" ) ) ;
2020-04-27 16:00:54 +00:00
try {
2020-04-27 15:12:13 +00:00
var json = atob ( cookie ) ;
2020-04-27 16:00:54 +00:00
data = JSON . parse ( json ) ;
} catch ( e ) {
2020-04-27 15:12:13 +00:00
console . log ( "Got " , e , " while reading info" ) ;
}
}
callback ( data ) ;
} else {
callback ( { } ) ;
2020-04-27 16:00:54 +00:00
}
2020-04-27 15:12:13 +00:00
}
function _set ( data ) {
if ( typeof chrome !== 'undefined' && chrome . storage ) {
console . log ( "SET chrome.storage.sync = " + JSON . stringify ( data ) ) ;
2020-04-27 16:00:54 +00:00
chrome . storage . sync . set ( { CONFIGS : data } ) ;
2020-04-27 15:12:13 +00:00
} else if ( typeof document != "undefined" ) {
document . cookie = "CONFIG=" + btoa ( JSON . stringify ( data ) ) ;
}
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
function loadConfiguration ( callback ) {
_get ( function ( value ) {
2020-04-27 16:00:54 +00:00
for ( var key in value ) {
2020-04-27 15:12:13 +00:00
if ( key == "set" ) continue ;
Espruino . Config [ key ] = value [ key ] ;
if ( Espruino . Core . Config . data [ key ] !== undefined &&
Espruino . Core . Config . data [ key ] . onChange !== undefined )
Espruino . Core . Config . data [ key ] . onChange ( value [ key ] ) ;
2020-04-27 16:00:54 +00:00
}
2020-04-27 15:12:13 +00:00
if ( callback !== undefined )
callback ( ) ;
} ) ;
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
function init ( ) {
addSection ( "General" , { sortOrder : 100 , description : "General Web IDE Settings" } ) ;
addSection ( "Communications" , { sortOrder : 200 , description : "Settings for communicating with the Espruino Board" } ) ;
addSection ( "Board" , { sortOrder : 300 , description : "Settings for the Espruino Board itself" } ) ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
function add ( name , options ) {
Espruino . Core . Config . data [ name ] = options ;
if ( Espruino . Config [ name ] === undefined )
Espruino . Config [ name ] = options . defaultValue ;
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
/ * * A d d a s e c t i o n ( o r i n f o r m a t i o n o n t h e p a g e ) .
* options = {
* sortOrder : int , // a number used for sorting
* description : "" ,
* getHTML : function ( callback ( html ) ) // optional
* } ;
* /
function addSection ( name , options ) {
options . name = name ;
builtinSections [ name ] = options ;
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
/** Get an object containing the information on a section used in configs */
function getSection ( name ) {
if ( builtinSections [ name ] !== undefined )
return builtinSections [ name ] ;
// not found - but we warned about this in getSections
return {
name : name
} ;
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
/** Get an object containing information on all 'sections' used in all the configs */
function getSections ( ) {
var sections = [ ] ;
// add sections we know about
for ( var name in builtinSections )
sections . push ( builtinSections [ name ] ) ;
// add other sections
for ( var i in Espruino . Core . Config . data ) {
var c = Espruino . Core . Config . data [ i ] ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
var found = false ;
for ( var s in sections )
if ( sections [ s ] . name == c . section )
found = true ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
if ( ! found ) {
console . warn ( "Section named " + c . section + " was not added with Config.addSection" ) ;
sections [ c . section ] = {
name : c . section ,
sortOrder : 0
2020-04-27 16:00:54 +00:00
} ;
2020-04-27 15:12:13 +00:00
}
}
// Now sort by sortOrder
sections . sort ( function ( a , b ) { return a . sortOrder - b . sortOrder ; } ) ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
return sections ;
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
Espruino . Config = { } ;
Espruino . Config . set = function ( key , value ) {
if ( Espruino . Config [ key ] != value ) {
Espruino . Config [ key ] = value ;
// Do the callback
if ( Espruino . Core . Config . data [ key ] !== undefined &&
Espruino . Core . Config . data [ key ] . onChange !== undefined )
Espruino . Core . Config . data [ key ] . onChange ( value ) ;
// Save to synchronized storage...
var data = { } ;
for ( var key in Espruino . Config )
if ( key != "set" )
2020-04-27 16:00:54 +00:00
data [ key ] = Espruino . Config [ key ] ;
_set ( data ) ;
2020-04-27 15:12:13 +00:00
}
} ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
function clearAll ( ) { // clear all settings
_set ( { } ) ;
for ( var name in Espruino . Core . Config . data ) {
var options = Espruino . Core . Config . data [ name ] ;
Espruino . Config [ name ] = options . defaultValue ;
}
}
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
Espruino . Core . Config = {
2020-04-27 16:00:54 +00:00
loadConfiguration : loadConfiguration , // special - called before init
2020-04-27 15:12:13 +00:00
init : init ,
add : add ,
data : { } ,
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
addSection : addSection ,
getSection : getSection ,
getSections : getSections ,
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
clearAll : clearAll , // clear all settings
} ;
2020-04-27 16:00:54 +00:00
2020-04-27 15:12:13 +00:00
} ) ( ) ;
/ *
Gordon Williams ( gw @ pur3 . co . uk )
Common entrypoint for all communications from the IDE . This handles
all serial _ * . js connection types and passes calls to the correct one .
To add a new serial device , you must add an object to
Espruino . Core . Serial . devices :
Espruino . Core . Serial . devices . push ( {
"name" : "Test" , // Name, when initialising
"init" : function ( ) // Gets called at startup
"getPorts" : function ( callback ) // calls 'callback' with an array of ports:
callback ( [ { path : "TEST" , // path passed to 'open' (and displayed to user)
description : "test" , // description displayed to user
type : "test" , // bluetooth|usb|socket - used to show icon in UI
// autoconnect : true // automatically conect to this (without the connect menu)
} ] , true ) ; // instantPorts - will getPorts return all the ports on the first call, or does it need multiple calls (eg. Bluetooth)
"open" : function ( path , openCallback , receiveCallback , disconnectCallback ) ,
"write" : function ( dataAsString , callbackWhenWritten )
"close" : function ( ) ,
"maxWriteLength" : 20 , // optional - the maximum amount of characters that should be given to 'write' at a time
} ) ;
* /
( function ( ) {
// List of ports and the devices they map to
var portToDevice = undefined ;
// The current connected device (from Espruino.Core.Serial.devices)
var currentDevice = undefined ;
// called when data received
var readListener = undefined ;
// are we sending binary data? If so, don't automatically insert breaks for stuff like Ctrl-C
var sendingBinary = false ;
// For throttled write
var slowWrite = true ;
var writeData = [ ] ;
var writeTimeout = undefined ;
/// flow control XOFF received - we shouldn't send anything
var flowControlXOFF = false ;
function init ( ) {
Espruino . Core . Config . add ( "BAUD_RATE" , {
section : "Communications" ,
name : "Baud Rate" ,
description : "When connecting over serial, this is the baud rate that is used. 9600 is the default for Espruino" ,
type : { 9600 : 9600 , 14400 : 14400 , 19200 : 19200 , 28800 : 28800 , 38400 : 38400 , 57600 : 57600 , 115200 : 115200 } ,
defaultValue : 9600 ,
} ) ;
Espruino . Core . Config . add ( "SERIAL_IGNORE" , {
section : "Communications" ,
name : "Ignore Serial Ports" ,
description : "A '|' separated list of serial port paths to ignore, eg `/dev/ttyS*|/dev/*.SOC`" ,
type : "string" ,
defaultValue : "/dev/ttyS*|/dev/*.SOC|/dev/*.MALS"
} ) ;
Espruino . Core . Config . add ( "SERIAL_FLOW_CONTROL" , {
section : "Communications" ,
name : "Software Flow Control" ,
description : "Respond to XON/XOFF flow control characters to throttle data uploads. By default Espruino sends XON/XOFF for USB and Bluetooth (on 2v05+)." ,
type : "boolean" ,
defaultValue : true
} ) ;
var devices = Espruino . Core . Serial . devices ;
for ( var i = 0 ; i < devices . length ; i ++ ) {
console . log ( " - Initialising Serial " + devices [ i ] . name ) ;
if ( devices [ i ] . init )
devices [ i ] . init ( ) ;
}
}
var startListening = function ( callback ) {
var oldListener = readListener ;
readListener = callback ;
return oldListener ;
} ;
/ * C a l l s ' c a l l b a c k ( p o r t _ l i s t , s h o u l d C a l l A g a i n ) '
'shouldCallAgain==true' means that more devices
may appear later on ( eg Bluetooth LE ) . * /
var getPorts = function ( callback ) {
var ports = [ ] ;
var newPortToDevice = [ ] ;
// get all devices
var responses = 0 ;
var devices = Espruino . Core . Serial . devices ;
if ( ! devices || devices . length == 0 ) {
portToDevice = newPortToDevice ;
return callback ( ports , false ) ;
}
var shouldCallAgain = false ;
devices . forEach ( function ( device ) {
//console.log("getPorts -->",device.name);
device . getPorts ( function ( devicePorts , instantPorts ) {
//console.log("getPorts <--",device.name);
if ( instantPorts === false ) shouldCallAgain = true ;
if ( devicePorts ) {
devicePorts . forEach ( function ( port ) {
var ignored = false ;
if ( Espruino . Config . SERIAL _IGNORE )
Espruino . Config . SERIAL _IGNORE . split ( "|" ) . forEach ( function ( wildcard ) {
var regexp = "^" + wildcard . replace ( /\./g , "\\." ) . replace ( /\*/g , ".*" ) + "$" ;
if ( port . path . match ( new RegExp ( regexp ) ) )
ignored = true ;
} ) ;
if ( ! ignored ) {
if ( port . usb && port . usb [ 0 ] == 0x0483 && port . usb [ 1 ] == 0x5740 )
port . description = "Espruino board" ;
ports . push ( port ) ;
newPortToDevice [ port . path ] = device ;
}
} ) ;
}
responses ++ ;
if ( responses == devices . length ) {
portToDevice = newPortToDevice ;
ports . sort ( function ( a , b ) {
if ( a . unimportant && ! b . unimportant ) return 1 ;
if ( b . unimportant && ! a . unimportant ) return - 1 ;
return 0 ;
} ) ;
callback ( ports , shouldCallAgain ) ;
}
} ) ;
} ) ;
} ;
var openSerial = function ( serialPort , connectCallback , disconnectCallback ) {
return openSerialInternal ( serialPort , connectCallback , disconnectCallback , 2 ) ;
}
var openSerialInternal = function ( serialPort , connectCallback , disconnectCallback , attempts ) {
/ * I f o p e n S e r i a l i s c a l l e d , w e n e e d t o h a v e c a l l e d g e t P o r t s f i r s t
in order to figure out which one of the serial _ implementations
we must call into . * /
if ( portToDevice === undefined ) {
portToDevice = [ ] ; // stop recursive calls if something errors
return getPorts ( function ( ) {
openSerialInternal ( serialPort , connectCallback , disconnectCallback , attempts ) ;
} ) ;
}
if ( ! ( serialPort in portToDevice ) ) {
if ( serialPort . toLowerCase ( ) in portToDevice ) {
serialPort = serialPort . toLowerCase ( ) ;
} else {
if ( attempts > 0 ) {
console . log ( "Port " + JSON . stringify ( serialPort ) + " not found - checking ports again (" + attempts + " attempts left)" ) ;
return getPorts ( function ( ) {
openSerialInternal ( serialPort , connectCallback , disconnectCallback , attempts - 1 ) ;
} ) ;
} else {
console . error ( "Port " + JSON . stringify ( serialPort ) + " not found" ) ;
return connectCallback ( undefined ) ;
}
}
}
connectionInfo = undefined ;
2020-04-27 16:00:54 +00:00
flowControlXOFF = false ;
2020-04-27 15:12:13 +00:00
currentDevice = portToDevice [ serialPort ] ;
currentDevice . open ( serialPort , function ( cInfo ) { // CONNECT
if ( ! cInfo ) {
// Espruino.Core.Notifications.error("Unable to connect");
console . error ( "Unable to open device (connectionInfo=" + cInfo + ")" ) ;
connectCallback ( undefined ) ;
} else {
connectionInfo = cInfo ;
connectedPort = serialPort ;
console . log ( "Connected" , cInfo ) ;
var portInfo = { port : serialPort } ;
if ( connectionInfo . portName )
portInfo . portName = connectionInfo . portName ;
Espruino . callProcessor ( "connected" , portInfo , function ( ) {
connectCallback ( cInfo ) ;
} ) ;
}
} , function ( data ) { // RECEIEVE DATA
if ( ! ( data instanceof ArrayBuffer ) ) console . warn ( "Serial port implementation is not returning ArrayBuffers" ) ;
if ( Espruino . Config . SERIAL _FLOW _CONTROL ) {
var u = new Uint8Array ( data ) ;
for ( var i = 0 ; i < u . length ; i ++ ) {
if ( u [ i ] == 17 ) { // XON
console . log ( "XON received => resume upload" ) ;
flowControlXOFF = false ;
}
if ( u [ i ] == 19 ) { // XOFF
console . log ( "XOFF received => pause upload" ) ;
flowControlXOFF = true ;
}
}
}
if ( readListener ) readListener ( data ) ;
} , function ( ) { // DISCONNECT
currentDevice = undefined ;
if ( ! connectionInfo ) {
// we got a disconnect when we hadn't connected...
// Just call connectCallback(undefined), don't bother sending disconnect
connectCallback ( undefined ) ;
return ;
2020-04-27 16:00:54 +00:00
}
2020-04-27 15:12:13 +00:00
connectionInfo = undefined ;
if ( writeTimeout !== undefined )
clearTimeout ( writeTimeout ) ;
writeTimeout = undefined ;
writeData = [ ] ;
sendingBinary = false ;
flowControlXOFF = false ;
Espruino . callProcessor ( "disconnected" , undefined , function ( ) {
disconnectCallback ( ) ;
} ) ;
} ) ;
} ;
var str2ab = function ( str ) {
var buf = new ArrayBuffer ( str . length ) ;
var bufView = new Uint8Array ( buf ) ;
for ( var i = 0 ; i < str . length ; i ++ ) {
var ch = str . charCodeAt ( i ) ;
if ( ch >= 256 ) {
console . warn ( "Attempted to send non-8 bit character - code " + ch ) ;
ch = "?" . charCodeAt ( 0 ) ;
}
bufView [ i ] = ch ;
}
return buf ;
} ;
var closeSerial = function ( ) {
if ( currentDevice ) {
currentDevice . close ( ) ;
currentDevice = undefined ;
} else
console . error ( "Close called, but serial port not open" ) ;
} ;
var isConnected = function ( ) {
return currentDevice !== undefined ;
} ;
var writeSerialWorker = function ( isStarting ) {
writeTimeout = undefined ; // we've been called
// check flow control
if ( flowControlXOFF ) {
/ * f l o w c o n t r o l w a s e n a b l e d - b i t h a c k y ( w e c o u l d u s e a c a l l b a c k )
but safe - just check again in a bit to see if we should send * /
writeTimeout = setTimeout ( function ( ) {
writeSerialWorker ( ) ;
} , 50 ) ;
return ;
}
// if we disconnected while sending, empty queue
if ( currentDevice === undefined ) {
if ( writeData [ 0 ] . callback )
writeData [ 0 ] . callback ( ) ;
writeData . shift ( ) ;
if ( writeData . length ) setTimeout ( function ( ) {
writeSerialWorker ( false ) ;
} , 1 ) ;
return ;
}
if ( writeData [ 0 ] . data === "" ) {
if ( writeData [ 0 ] . showStatus )
Espruino . Core . Status . setStatus ( "Sent" ) ;
if ( writeData [ 0 ] . callback )
writeData [ 0 ] . callback ( ) ;
writeData . shift ( ) ; // remove this empty first element
if ( ! writeData . length ) return ; // anything left to do?
isStarting = true ;
}
if ( isStarting ) {
var blockSize = 512 ;
if ( currentDevice . maxWriteLength )
blockSize = currentDevice . maxWriteLength ;
/ * i f w e ' r e t h r o t t l i n g o u r w r i t e s w e w a n t t o s e n d s m a l l
* blocks of data at once . We still limit the size of
* sent blocks to 512 because on Mac we seem to lose
* data otherwise ( not on any other platforms ! ) * /
if ( slowWrite ) blockSize = 19 ;
writeData [ 0 ] . blockSize = blockSize ;
writeData [ 0 ] . showStatus &= writeData [ 0 ] . data . length > writeData [ 0 ] . blockSize ;
if ( writeData [ 0 ] . showStatus ) {
Espruino . Core . Status . setStatus ( "Sending..." , writeData [ 0 ] . data . length ) ;
console . log ( "---> " + JSON . stringify ( writeData [ 0 ] . data ) ) ;
}
}
// Initial split use previous, or don't
var d = undefined ;
var split = writeData [ 0 ] . nextSplit || { start : 0 , end : writeData [ 0 ] . data . length , delay : 0 } ;
// if we get something like Ctrl-C or `reset`, wait a bit for it to complete
if ( ! sendingBinary ) {
function findSplitIdx ( prev , substr , delay , reason ) {
var match = writeData [ 0 ] . data . match ( substr ) ;
// not found
if ( match === null ) return prev ;
// or previous find was earlier in str
var end = match . index + match [ 0 ] . length ;
if ( end > prev . end ) return prev ;
// found, and earlier
prev . start = match . index ;
prev . end = end ;
prev . delay = delay ;
prev . match = match [ 0 ] ;
prev . reason = reason ;
return prev ;
}
split = findSplitIdx ( split , /\x03/ , 250 , "Ctrl-C" ) ; // Ctrl-C
split = findSplitIdx ( split , /reset\(\);\n/ , 250 , "reset()" ) ; // Reset
split = findSplitIdx ( split , /load\(\);\n/ , 250 , "load()" ) ; // Load
split = findSplitIdx ( split , /Modules.addCached\("[^\n]*"\);\n/ , 250 , "Modules.addCached" ) ; // Adding a module
split = findSplitIdx ( split , /\x10require\("Storage"\).write\([^\n]*\);\n/ , 500 , "Storage.write" ) ; // Write chunk of data
}
// Otherwise split based on block size
if ( ! split . match || split . end >= writeData [ 0 ] . blockSize ) {
if ( split . match ) writeData [ 0 ] . nextSplit = split ;
split = { start : 0 , end : writeData [ 0 ] . blockSize , delay : 0 } ;
}
if ( split . match ) console . log ( "Splitting for " + split . reason + ", delay " + split . delay ) ;
// Only send some of the data
if ( writeData [ 0 ] . data . length > split . end ) {
if ( slowWrite && split . delay == 0 ) split . delay = 50 ;
d = writeData [ 0 ] . data . substr ( 0 , split . end ) ;
writeData [ 0 ] . data = writeData [ 0 ] . data . substr ( split . end ) ;
if ( writeData [ 0 ] . nextSplit ) {
writeData [ 0 ] . nextSplit . start -= split . end ;
writeData [ 0 ] . nextSplit . end -= split . end ;
if ( writeData [ 0 ] . nextSplit . end <= 0 )
writeData [ 0 ] . nextSplit = undefined ;
}
} else {
d = writeData [ 0 ] . data ;
writeData [ 0 ] . data = "" ;
writeData [ 0 ] . nextSplit = undefined ;
}
// update status
if ( writeData [ 0 ] . showStatus )
Espruino . Core . Status . incrementProgress ( d . length ) ;
// actually write data
//console.log("Sending block "+JSON.stringify(d)+", wait "+split.delay+"ms");
currentDevice . write ( d , function ( ) {
// Once written, start timeout
writeTimeout = setTimeout ( function ( ) {
writeSerialWorker ( ) ;
} , split . delay ) ;
} ) ;
}
// Throttled serial write
var writeSerial = function ( data , showStatus , callback ) {
if ( showStatus === undefined ) showStatus = true ;
/ * Q u e u e o u r d a t a t o w r i t e . I f t h e r e w a s p r e v i o u s d a t a a n d n o c a l l b a c k t o
invoke on this data or the previous then just append data . This would happen
if typing in the terminal for example . * /
if ( ! callback && writeData . length && ! writeData [ writeData . length - 1 ] . callback ) {
writeData [ writeData . length - 1 ] . data += data ;
} else {
writeData . push ( { data : data , callback : callback , showStatus : showStatus } ) ;
/ * i f t h i s i s o u r f i r s t d a t a , s t a r t s e n d i n g n o w . O t h e r w i s e w e ' r e a l r e a d y
busy sending and will pull data off writeData when ready * /
if ( writeData . length == 1 )
writeSerialWorker ( true ) ;
}
} ;
// ----------------------------------------------------------
Espruino . Core . Serial = {
"devices" : [ ] , // List of devices that can provide a serial API
"init" : init ,
"getPorts" : getPorts ,
"open" : openSerial ,
"isConnected" : isConnected ,
"startListening" : startListening ,
"write" : writeSerial ,
"close" : closeSerial ,
"isSlowWrite" : function ( ) { return slowWrite ; } ,
"setSlowWrite" : function ( isOn , force ) {
if ( ( ! force ) && Espruino . Config . SERIAL _THROTTLE _SEND ) {
console . log ( "ForceThrottle option is set - set Slow Write = true" ) ;
isOn = true ;
} else
console . log ( "Set Slow Write = " + isOn ) ;
slowWrite = isOn ;
} ,
"setBinary" : function ( isOn ) {
sendingBinary = isOn ;
}
} ;
} ) ( ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
The plugin that actually writes code out to Espruino
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
function init ( ) {
Espruino . Core . Config . add ( "RESET_BEFORE_SEND" , {
section : "Communications" ,
name : "Reset before Send" ,
description : "Reset Espruino before sending code from the editor pane?" ,
type : "boolean" ,
defaultValue : true
} ) ;
Espruino . Core . Config . add ( "STORE_LINE_NUMBERS" , {
section : "Communications" ,
name : "Store line numbers" ,
description : "Should Espruino store line numbers for each function? This uses one extra variable per function, but allows you to get source code debugging in the Web IDE" ,
type : "boolean" ,
defaultValue : true
} ) ;
}
function writeToEspruino ( code , callback ) {
/ * h a c k a r o u n d n o n - K & R c o d e f o r m a t t i n g t h a t w o u l d h a v e
broken Espruino CLI ' s bracket counting * /
code = reformatCode ( code ) ;
if ( code === undefined ) return ; // it should already have errored
// We want to make sure we've got a prompt before sending. If not,
// this will issue a Ctrl+C
Espruino . Core . Utils . getEspruinoPrompt ( function ( ) {
// Make sure code ends in 2 newlines
while ( code [ code . length - 2 ] != "\n" || code [ code . length - 1 ] != "\n" )
code += "\n" ;
// If we're supposed to reset Espruino before sending...
if ( Espruino . Config . RESET _BEFORE _SEND ) {
code = "\x10reset();\n" + code ;
}
//console.log("Sending... "+data);
Espruino . Core . Serial . write ( code , true , function ( ) {
// give 5 seconds for sending with save and 2 seconds without save
var count = Espruino . Config . SAVE _ON _SEND ? 50 : 20 ;
setTimeout ( function cb ( ) {
if ( Espruino . Core . Terminal !== undefined &&
Espruino . Core . Terminal . getTerminalLine ( ) != ">" ) {
count -- ;
if ( count > 0 ) {
setTimeout ( cb , 100 ) ;
} else {
Espruino . Core . Notifications . error ( "Prompt not detected - upload failed. Trying to recover..." ) ;
Espruino . Core . Serial . write ( "\x03\x03echo(1)\n" , false , callback ) ;
}
} else {
if ( callback ) callback ( ) ;
}
} , 100 ) ;
} ) ;
} ) ;
} ;
/// Parse and fix issues like `if (false)\n foo` in the root scope
function reformatCode ( code ) {
var APPLY _LINE _NUMBERS = false ;
var lineNumberOffset = 0 ;
var ENV = Espruino . Core . Env . getData ( ) ;
if ( ENV && ENV . VERSION _MAJOR && ENV . VERSION _MINOR ) {
if ( ENV . VERSION _MAJOR > 1 ||
ENV . VERSION _MINOR >= 81.086 ) {
if ( Espruino . Config . STORE _LINE _NUMBERS )
APPLY _LINE _NUMBERS = true ;
}
}
// Turn cr/lf into just lf (eg. windows -> unix)
code = code . replace ( /\r\n/g , "\n" ) ;
// First off, try and fix funky characters
for ( var i = 0 ; i < code . length ; i ++ ) {
var ch = code . charCodeAt ( i ) ;
if ( ( ch < 32 || ch > 255 ) && ch != 9 /*Tab*/ && ch != 10 /*LF*/ && ch != 13 /*CR*/ ) {
console . warn ( "Funky character code " + ch + " at position " + i + ". Replacing with ?" ) ;
code = code . substr ( 0 , i ) + "?" + code . substr ( i + 1 ) ;
}
}
/ * S e a r c h f o r l i n e s a d d e d t o t h e s t a r t o f t h e c o d e b y t h e m o d u l e h a n d l e r .
Ideally there would be a better way of doing this so line numbers stayed correct ,
but this hack works for now . Fixes EspruinoWebIDE # 140 * /
if ( APPLY _LINE _NUMBERS ) {
var l = code . split ( "\n" ) ;
var i = 0 ;
while ( l [ i ] && ( l [ i ] . substr ( 0 , 8 ) == "Modules." ||
l [ i ] . substr ( 0 , 8 ) == "setTime(" ) ) i ++ ;
lineNumberOffset = - i ;
}
var resultCode = "\x10" ; // 0x10 = echo off for line
/ * * w e ' r e l o o k i n g f o r :
* ` a = \n b `
* ` for (.....) \n X `
* ` if (.....) \n X `
* ` if (.....) { } \n else foo `
* ` while (.....) \n X `
* ` do \n X `
* ` function (.....) \n X `
* ` function N(.....) \n X `
* ` var a \n , b ` ` var a = 0 \n , b `
* ` var a, \n b ` ` var a = 0, \n b `
* ` a \n . b `
* ` foo() \n . b `
* ` try { } \n catch \n () \n {} `
*
* These are divided into two groups - where there are brackets
* after the keyword ( statementBeforeBrackets ) and where there aren ' t
* ( statement )
*
* We fix them by replacing \ n with what you get when you press
* Alt + Enter ( Ctrl + LF ) . This tells Espruino that it ' s a newline
* but NOT to execute .
* /
var lex = Espruino . Core . Utils . getLexer ( code ) ;
var brackets = 0 ;
var curlyBrackets = 0 ;
var statementBeforeBrackets = false ;
var statement = false ;
var varDeclaration = false ;
var lastIdx = 0 ;
var lastTok = { str : "" } ;
var tok = lex . next ( ) ;
while ( tok !== undefined ) {
var previousString = code . substring ( lastIdx , tok . startIdx ) ;
var tokenString = code . substring ( tok . startIdx , tok . endIdx ) ;
//console.log("prev "+JSON.stringify(previousString)+" next "+tokenString);
/ * I n s e r t i n g A l t - E n t e r n e w l i n e , w h i c h a d d s n e w l i n e w i t h o u t t r y i n g
to execute * /
if ( brackets > 0 || // we have brackets - sending the alt-enter special newline means Espruino doesn't have to do a search itself - faster.
statement || // statement was before brackets - expecting something else
statementBeforeBrackets || // we have an 'if'/etc
varDeclaration || // variable declaration then newline
tok . str == "," || // comma on newline - there was probably something before
tok . str == "." || // dot on newline - there was probably something before
tok . str == "+" || tok . str == "-" || // +/- on newline - there was probably something before
tok . str == "=" || // equals on newline - there was probably something before
tok . str == "else" || // else on newline
lastTok . str == "else" || // else befgore newline
tok . str == "catch" || // catch on newline - part of try..catch
lastTok . str == "catch"
) {
//console.log("Possible"+JSON.stringify(previousString));
previousString = previousString . replace ( /\n/g , "\x1B\x0A" ) ;
}
var previousBrackets = brackets ;
if ( tok . str == "(" || tok . str == "{" || tok . str == "[" ) brackets ++ ;
if ( tok . str == "{" ) curlyBrackets ++ ;
if ( tok . str == ")" || tok . str == "}" || tok . str == "]" ) brackets -- ;
if ( tok . str == "}" ) curlyBrackets -- ;
if ( brackets == 0 ) {
if ( tok . str == "for" || tok . str == "if" || tok . str == "while" || tok . str == "function" || tok . str == "throw" ) {
statementBeforeBrackets = true ;
varDeclaration = false ;
} else if ( tok . str == "var" ) {
varDeclaration = true ;
} else if ( tok . type == "ID" && lastTok . str == "function" ) {
statementBeforeBrackets = true ;
} else if ( tok . str == "try" || tok . str == "catch" ) {
statementBeforeBrackets = true ;
} else if ( tok . str == ")" && statementBeforeBrackets ) {
statementBeforeBrackets = false ;
statement = true ;
} else if ( [ "=" , "^" , "&&" , "||" , "+" , "+=" , "-" , "-=" , "*" , "*=" , "/" , "/=" , "%" , "%=" , "&" , "&=" , "|" , "|=" ] . indexOf ( tok . str ) >= 0 ) {
statement = true ;
} else {
if ( tok . str == ";" ) varDeclaration = false ;
statement = false ;
statementBeforeBrackets = false ;
}
}
/ * I f w e ' r e a t r o o t s c o p e a n d h a d w h i t e s p a c e / c o m m e n t s b e t w e e n c o d e ,
remove it all and replace it with a single newline and a
0x10 ( echo off for line ) character . However DON ' T do this if we had
an alt - enter in the line , as it was there to stop us executing
prematurely * /
if ( previousBrackets == 0 &&
previousString . indexOf ( "\n" ) >= 0 &&
previousString . indexOf ( "\x1B\x0A" ) < 0 ) {
previousString = "\n\x10" ;
// Apply line numbers to each new line sent, to aid debugger
if ( APPLY _LINE _NUMBERS && tok . lineNumber && ( tok . lineNumber + lineNumberOffset ) > 0 ) {
// Esc [ 1234 d
// This is the 'set line number' command that we're abusing :)
previousString += "\x1B\x5B" + ( tok . lineNumber + lineNumberOffset ) + "d" ;
}
}
// add our stuff back together
resultCode += previousString + tokenString ;
// next
lastIdx = tok . endIdx ;
lastTok = tok ;
tok = lex . next ( ) ;
}
//console.log(resultCode);
if ( brackets > 0 ) {
Espruino . Core . Notifications . error ( "You have more open brackets than close brackets. Please see the hints in the Editor window." ) ;
return undefined ;
}
if ( brackets < 0 ) {
Espruino . Core . Notifications . error ( "You have more close brackets than open brackets. Please see the hints in the Editor window." ) ;
return undefined ;
}
return resultCode ;
} ;
Espruino . Core . CodeWriter = {
init : init ,
writeToEspruino : writeToEspruino ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Automatically load any referenced modules
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
function init ( ) {
Espruino . Core . Config . add ( "MODULE_URL" , {
section : "Communications" ,
name : "Module URL" ,
description : "Where to search online for modules when `require()` is used" ,
type : "string" ,
defaultValue : "https://www.espruino.com/modules"
} ) ;
Espruino . Core . Config . add ( "MODULE_EXTENSIONS" , {
section : "Communications" ,
name : "Module Extensions" ,
description : "The file extensions to use for each module. These are checked in order and the first that exists is used. One or more file extensions (including the dot) separated by `|`" ,
type : "string" ,
defaultValue : ".min.js|.js"
} ) ;
Espruino . Core . Config . add ( "MODULE_AS_FUNCTION" , {
section : "Communications" ,
name : "Modules uploaded as functions" ,
description : "Espruino 1v90 and later ONLY. Upload modules as Functions, allowing any functions inside them to be loaded directly from flash when 'Save on Send' is enabled." ,
type : "boolean" ,
defaultValue : true
} ) ;
Espruino . Core . Config . add ( "MODULE_PROXY_ENABLED" , {
section : "Communications" ,
name : "Enable Proxy" ,
description : "Enable Proxy for loading the modules when `require()` is used (only in native IDE)" ,
type : "boolean" ,
defaultValue : false
} ) ;
Espruino . Core . Config . add ( "MODULE_PROXY_URL" , {
section : "Communications" ,
name : "Proxy URL" ,
description : "Proxy URL for loading the modules when `require()` is used (only in native IDE)" ,
type : "string" ,
defaultValue : ""
} ) ;
Espruino . Core . Config . add ( "MODULE_PROXY_PORT" , {
section : "Communications" ,
name : "Proxy Port" ,
description : "Proxy Port for loading the modules when `require()` is used (only in native IDE)" ,
type : "string" ,
defaultValue : ""
} ) ;
// When code is sent to Espruino, search it for modules and add extra code required to load them
Espruino . addProcessor ( "transformForEspruino" , function ( code , callback ) {
2020-04-27 16:00:54 +00:00
if ( Espruino . Config . ROLLUP ) {
return loadModulesRollup ( code , callback ) ;
}
2020-04-27 15:12:13 +00:00
loadModules ( code , callback ) ;
} ) ;
// Append the 'getModule' processor as the last (plugins get initialized after Espruino.Core modules)
Espruino . Plugins . CoreModules = {
init : function ( ) {
Espruino . addProcessor ( "getModule" , function ( data , callback ) {
if ( data . moduleCode !== undefined ) { // already provided be previous getModule processor
return callback ( data ) ;
}
fetchGetModule ( data , callback ) ;
} ) ;
}
} ;
}
function isBuiltIn ( module ) {
var d = Espruino . Core . Env . getData ( ) ;
// If we got data from the device itself, use that as the
// definitive answer
if ( "string" == typeof d . MODULES )
return d . MODULES . split ( "," ) . indexOf ( module ) >= 0 ;
// Otherwise try and figure it out from JSON
if ( "info" in d &&
"builtin_modules" in d . info &&
d . info . builtin _modules . indexOf ( module ) >= 0 )
return true ;
// Otherwise assume we don't have it
return false ;
}
/** Find any instances of require(...) in the code string and return a list */
var getModulesRequired = function ( code ) {
var modules = [ ] ;
var lex = Espruino . Core . Utils . getLexer ( code ) ;
var tok = lex . next ( ) ;
var state = 0 ;
while ( tok !== undefined ) {
if ( state == 0 && tok . str == "require" ) {
state = 1 ;
} else if ( state == 1 && tok . str == "(" ) {
state = 2 ;
} else if ( state == 2 && ( tok . type == "STRING" ) ) {
state = 0 ;
var module = tok . value ;
if ( ! isBuiltIn ( module ) && modules . indexOf ( module ) < 0 )
modules . push ( module ) ;
} else
state = 0 ;
tok = lex . next ( ) ;
}
return modules ;
} ;
/** Download modules from MODULE_URL/.. */
function fetchGetModule ( data , callback ) {
var fullModuleName = data . moduleName ;
// try and load the module the old way...
console . log ( "loadModule(" + fullModuleName + ")" ) ;
var urls = [ ] ; // Array of where to look for this module
var modName ; // Simple name of the module
if ( Espruino . Core . Utils . isURL ( fullModuleName ) ) {
modName = fullModuleName . substr ( fullModuleName . lastIndexOf ( "/" ) + 1 ) . split ( "." ) [ 0 ] ;
urls = [ fullModuleName ] ;
} else {
modName = fullModuleName ;
Espruino . Config . MODULE _URL . split ( "|" ) . forEach ( function ( url ) {
url = url . trim ( ) ;
if ( url . length != 0 )
Espruino . Config . MODULE _EXTENSIONS . split ( "|" ) . forEach ( function ( extension ) {
urls . push ( url + "/" + fullModuleName + extension ) ;
} )
} ) ;
} ;
// Recursively go through all the urls
( function download ( urls ) {
if ( urls . length == 0 ) {
return callback ( data ) ;
}
var dlUrl = urls [ 0 ] ;
Espruino . Core . Utils . getURL ( dlUrl , function ( code ) {
if ( code !== undefined ) {
// we got it!
data . moduleCode = code ;
data . isMinified = dlUrl . substr ( - 7 ) == ".min.js" ;
return callback ( data ) ;
} else {
// else try next
download ( urls . slice ( 1 ) ) ;
}
} ) ;
} ) ( urls ) ;
}
/ * * C a l l e d f r o m l o a d M o d u l e w h e n a m o d u l e i s l o a d e d . P a r s e i t f o r o t h e r m o d u l e s i t m i g h t u s e
* and resolve dfd after all submodules have been loaded * /
function moduleLoaded ( resolve , requires , modName , data , loadedModuleData , alreadyMinified ) {
// Check for any modules used from this module that we don't already have
var newRequires = getModulesRequired ( data ) ;
console . log ( " - " + modName + " requires " + JSON . stringify ( newRequires ) ) ;
// if we need new modules, set them to load and get their promises
var newPromises = [ ] ;
for ( var i in newRequires ) {
if ( requires . indexOf ( newRequires [ i ] ) < 0 ) {
console . log ( " Queueing " + newRequires [ i ] ) ;
requires . push ( newRequires [ i ] ) ;
newPromises . push ( loadModule ( requires , newRequires [ i ] , loadedModuleData ) ) ;
} else {
console . log ( " Already loading " + newRequires [ i ] ) ;
}
}
var loadProcessedModule = function ( module ) {
// if we needed to load something, wait until it's loaded before resolving this
Promise . all ( newPromises ) . then ( function ( ) {
// add the module to end of our array
if ( Espruino . Config . MODULE _AS _FUNCTION )
loadedModuleData . push ( "Modules.addCached(" + JSON . stringify ( module . name ) + ",function(){" + module . code + "});" ) ;
else
loadedModuleData . push ( "Modules.addCached(" + JSON . stringify ( module . name ) + "," + JSON . stringify ( module . code ) + ");" ) ;
// We're done
resolve ( ) ;
} ) ;
}
if ( alreadyMinified )
loadProcessedModule ( { code : data , name : modName } ) ;
else
Espruino . callProcessor ( "transformModuleForEspruino" , { code : data , name : modName } , loadProcessedModule ) ;
}
/ * * G i v e n a m o d u l e n a m e ( w h i c h c o u l d b e a U R L ) , t r y a n d f i n d i t . R e t u r n
* a deferred thingybob which signals when we ' re done . * /
function loadModule ( requires , fullModuleName , loadedModuleData ) {
return new Promise ( function ( resolve , reject ) {
// First off, try and find this module using callProcessor
Espruino . callProcessor ( "getModule" ,
{ moduleName : fullModuleName , moduleCode : undefined , isMinified : false } ,
function ( data ) {
if ( data . moduleCode === undefined ) {
Espruino . Core . Notifications . warning ( "Module " + fullModuleName + " not found" ) ;
return resolve ( ) ;
}
// great! it found something. Use it.
moduleLoaded ( resolve , requires , fullModuleName , data . moduleCode , loadedModuleData , data . isMinified ) ;
} ) ;
} ) ;
}
/ * * F i n d s i n s t a n c e s o f ' r e q u i r e ' a n d t h e n e n s u r e s t h a t
those modules are loaded into the module cache beforehand
( by inserting the relevant 'addCached' commands into 'code' * /
function loadModules ( code , callback ) {
var loadedModuleData = [ ] ;
var requires = getModulesRequired ( code ) ;
if ( requires . length == 0 ) {
// no modules needed - just return
callback ( code ) ;
} else {
Espruino . Core . Status . setStatus ( "Loading modules" ) ;
// Kick off the module loading (each returns a promise)
var promises = requires . map ( function ( moduleName ) {
return loadModule ( requires , moduleName , loadedModuleData ) ;
} ) ;
// When all promises are complete
Promise . all ( promises ) . then ( function ( ) {
callback ( loadedModuleData . join ( "\n" ) + "\n" + code ) ;
} ) ;
}
2020-04-27 16:00:54 +00:00
}
function loadModulesRollup ( code , callback ) {
rollupTools . loadModulesRollup ( code )
. then ( generated => {
const minified = generated . code ;
console . log ( 'rollup: ' + minified . length + ' bytes' ) ;
2020-04-27 15:12:13 +00:00
2020-04-27 16:00:54 +00:00
// FIXME: needs warnings?
Espruino . Core . Notifications . info ( 'Rollup no errors. Bundling ' + code . length + ' bytes to ' + minified . length + ' bytes' ) ;
callback ( minified ) ;
} )
. catch ( err => {
console . log ( 'rollup:error' , err ) ;
Espruino . Core . Notifications . error ( "Rollup errors - Bundling failed: " + String ( err ) . trim ( ) ) ;
callback ( code ) ;
} ) ;
}
2020-04-27 15:12:13 +00:00
Espruino . Core . Modules = {
init : init
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Board Environment variables ( process . env ) - queried when board connects
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
var environmentData = { } ;
var boardData = { } ;
function init ( ) {
Espruino . Core . Config . add ( "ENV_ON_CONNECT" , {
section : "Communications" ,
name : "Request board details on connect" ,
description : 'Just after the board is connected, should we query `process.env` to find out which board we\'re connected to? ' +
'This enables the Web IDE\'s code completion, compiler features, and firmware update notice.' ,
type : "boolean" ,
defaultValue : true ,
} ) ;
Espruino . addProcessor ( "connected" , function ( data , callback ) {
// Give us some time for any stored data to come in
setTimeout ( queryBoardProcess , 200 , data , callback ) ;
} ) ;
}
function queryBoardProcess ( data , callback ) {
if ( ( ! Espruino . Config . ENV _ON _CONNECT ) ||
( Espruino . Core . MenuFlasher && Espruino . Core . MenuFlasher . isFlashing ( ) ) ) {
return callback ( data ) ;
}
Espruino . Core . Utils . executeExpression ( "process.env" , function ( result ) {
var json = { } ;
if ( result !== undefined ) {
try {
json = JSON . parse ( result ) ;
} catch ( e ) {
console . log ( "JSON parse failed - " + e + " in " + JSON . stringify ( result ) ) ;
}
}
if ( Object . keys ( json ) . length == 0 ) {
Espruino . Core . Notifications . error ( "Unable to retrieve board information.\nConnection Error?" ) ;
// make sure we don't remember a previous board's info
json = {
VERSION : undefined ,
BOARD : undefined ,
MODULES : undefined ,
EXPTR : undefined
} ;
} else {
if ( json . BOARD && json . VERSION )
Espruino . Core . Notifications . info ( "Found " + json . BOARD + ", " + json . VERSION ) ;
}
// now process the enviroment variables
for ( var k in json ) {
boardData [ k ] = json [ k ] ;
environmentData [ k ] = json [ k ] ;
}
if ( environmentData . VERSION ) {
var v = environmentData . VERSION ;
var vIdx = v . indexOf ( "v" ) ;
if ( vIdx >= 0 ) {
environmentData . VERSION _MAJOR = parseInt ( v . substr ( 0 , vIdx ) ) ;
var minor = v . substr ( vIdx + 1 ) ;
var dot = minor . indexOf ( "." ) ;
if ( dot >= 0 )
environmentData . VERSION _MINOR = parseInt ( minor . substr ( 0 , dot ) ) + parseInt ( minor . substr ( dot + 1 ) ) * 0.001 ;
else
environmentData . VERSION _MINOR = parseFloat ( minor ) ;
}
}
Espruino . callProcessor ( "environmentVar" , environmentData , function ( envData ) {
environmentData = envData ;
callback ( data ) ;
} ) ;
} ) ;
}
/** Get all data merged in from the board */
function getData ( ) {
return environmentData ;
}
/** Get just the board's environment data */
function getBoardData ( ) {
return boardData ;
}
/** Get a list of boards that we know about */
function getBoardList ( callback ) {
var jsonDir = Espruino . Config . BOARD _JSON _URL ;
// ensure jsonDir ends with slash
if ( jsonDir . indexOf ( '/' , jsonDir . length - 1 ) === - 1 ) {
jsonDir += '/' ;
}
Espruino . Core . Utils . getJSONURL ( jsonDir + "boards.json" , function ( boards ) {
// now load all the individual JSON files
var promises = [ ] ;
for ( var boardId in boards ) {
promises . push ( ( function ( ) {
var id = boardId ;
return new Promise ( function ( resolve , reject ) {
Espruino . Core . Utils . getJSONURL ( jsonDir + boards [ boardId ] . json , function ( data ) {
boards [ id ] [ "json" ] = data ;
resolve ( ) ;
} ) ;
} ) ;
} ) ( ) ) ;
}
// When all are loaded, load the callback
Promise . all ( promises ) . then ( function ( ) {
callback ( boards ) ;
} ) ;
} ) ;
}
Espruino . Core . Env = {
init : init ,
getData : getData ,
getBoardData : getBoardData ,
getBoardList : getBoardList ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
2020-04-29 08:20:18 +00:00
2020-04-27 15:12:13 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2020-04-29 08:20:18 +00:00
Automatically run an assembler on inline assembler statements
2020-04-27 15:12:13 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
2020-04-29 08:20:18 +00:00
var base64 _encode ;
if ( typeof btoa == "undefined" )
base64 _encode = function ( s ) { return Buffer . from ( s ) . toString ( 'base64' ) ; } ;
else
base64 _encode = btoa ;
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
/ * T h u m b r e f e r e n c e :
http : //ece.uwaterloo.ca/~ece222/ARM/ARM7-TDMI-manual-pt3.pdf
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
ARM reference
https : //web.eecs.umich.edu/~prabal/teaching/eecs373-f11/readings/ARMv7-M_ARM.pdf
* /
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
// list of registers (for push/pop type commands)
function rlist _lr ( value ) {
var regs = value . split ( "," ) ;
var vals = { r0 : 1 , r1 : 2 , r2 : 4 , r3 : 8 , r4 : 16 , r5 : 32 , r6 : 64 , r7 : 128 , lr : 256 } ;
var bits = 0 ;
for ( var i in regs ) {
var reg = regs [ i ] . trim ( ) ;
if ( ! ( reg in vals ) ) throw "Unknown register name " + reg ;
bits |= vals [ reg ] ;
}
return bits ;
}
function reg ( reg _offset ) {
return function ( reg ) {
var vals = { r0 : 0 , r1 : 1 , r2 : 2 , r3 : 3 , r4 : 4 , r5 : 5 , r6 : 6 , r7 : 7 } ;
if ( ! ( reg in vals ) ) throw "Unknown register name " + reg ;
return vals [ reg ] << reg _offset ;
} ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
function reg4 ( reg _offset ) { // 4 bit register
return function ( reg ) {
var vals = { r0 : 0 , r1 : 1 , r2 : 2 , r3 : 3 , r4 : 4 , r5 : 5 , r6 : 6 , r7 : 7 ,
r8 : 8 , r9 : 9 , r10 : 10 , r11 : 11 , r12 : 12 , r13 : 13 , r14 : 14 , r15 : 15 ,
lr : 14 , pc : 15 } ;
if ( ! ( reg in vals ) ) throw "Unknown register name " + reg ;
return vals [ reg ] << reg _offset ;
} ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
function reg _or _immediate ( reg _offset , immediate _bit ) {
return function ( reg ) {
var regVal = parseInt ( reg ) ;
if ( regVal >= 0 && regVal < 8 )
return ( ( regVal & 7 ) << reg _offset ) | ( 1 << immediate _bit ) ;
var vals = { r0 : 0 , r1 : 1 , r2 : 2 , r3 : 3 , r4 : 4 , r5 : 5 , r6 : 6 , r7 : 7 } ;
if ( ! ( reg in vals ) ) throw "Unknown register name, or immediate out of range 0..7 " + reg ;
return vals [ reg ] << reg _offset ;
} ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
function reg _base _offset ( base _offset , offset _offset ) {
return function ( value ) {
var parms = value . split ( "," ) ;
return reg ( base _offset ) ( parms [ 0 ] ) | reg ( offset _offset ) ( parms [ 0 ] ) ;
} ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function thumb2 _immediate _t3 ( value ) {
if ( value [ 0 ] != "#" )
throw new "Expecting '#' before number" ;
var v = parseInt ( value . substr ( 1 ) ) ;
if ( v >= 0 && v < 65536 ) {
// https://web.eecs.umich.edu/~prabal/teaching/eecs373-f11/readings/ARMv7-M_ARM.pdf page 347
var imm4 , i , imm3 , imm8 ; // what the...?
imm4 = ( v >> 12 ) & 15 ;
i = ( v >> 11 ) & 1 ;
imm3 = ( v >> 8 ) & 7 ;
imm8 = v & 255 ;
return ( i << 26 ) | ( imm4 << 16 ) | ( imm3 << 12 ) | imm8 ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
throw "Invalid number '" + value + "' - must be between 0 and 65535" ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function _int ( offset , bits , shift , signed ) {
return function ( value , labels ) {
var maxValue = ( ( 1 << bits ) - 1 ) << shift ;
var minValue = 0 ;
if ( signed ) {
minValue = - ( 1 << ( bits - 1 ) ) ;
maxValue += minValue ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
var binValue = undefined ;
if ( value [ 0 ] == "#" ) {
binValue = parseInt ( value . substr ( 1 ) ) ;
} else {
var addValue = 0 ;
var maths = value . indexOf ( "+" ) ;
if ( maths >= 0 ) {
addValue = parseInt ( value . substr ( maths ) ) ;
value = value . substr ( 0 , maths ) ;
}
if ( value in labels )
binValue = labels [ value ] + addValue - labels [ "PC" ] ;
else
throw "Unknown label '" + value + "'" ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
//console.log("VALUE----------- "+binValue+" PC "+labels["PC"]+" L "+labels[value]);
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
if ( binValue >= minValue && binValue <= maxValue && ( binValue & ( ( 1 << shift ) - 1 ) ) == 0 )
return ( ( binValue >> shift ) & ( ( 1 << bits ) - 1 ) ) << offset ;
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
var msg = "Invalid number '" + value + "' (" + binValue + ") - must be between 0 and " + maxValue ;
if ( shift != 0 ) msg += " and a multiple of " + ( 1 << shift ) ;
throw msg ;
} ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function uint ( offset , bits , shift ) {
return _int ( offset , bits , shift , false ) ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function sint ( offset , bits , shift ) {
return _int ( offset , bits , shift , true ) ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
// special 23-bit address (bottom bit ignored) split into two halves
function bl _addr ( ) {
var normal = sint ( 0 , 22 , 1 ) ; // un-split address
return function ( value , labels ) {
var v = normal ( value , labels ) ;
return ( ( v >> 11 ) & 0x7FF ) << 16 | ( v & 0x7FF ) ;
} ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
var ops = {
// Format 1: move shifted register
"lsl" : [ { base : "00000-----___---" , regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 0 ) ] } ,
{ base : "0100000010___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // 5.4 d = d << s
"lsr" : [ { base : "00001-----___---" , regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 0 ) ] } ,
{ base : "0100000011___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // 5.4 d = d >> s
"asr" : [ { base : "00010-----___---" , regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 0 ) ] } ,
{ base : "0100000100___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // 5.4 d = d >>> s
// 5.2 Format 2: add/subtract
// 00011
// 5.3 Format 3: move/compare/add/subtract immediate
"cmp" : [ { base : "00101---________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } , // move/compare/subtract immediate
{ base : "0100001010___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // 5.4 test d-s
// 5.4 Format 4: ALU operations
"and" : [ { base : "0100000000___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] ,
"eor" : [ { base : "0100000001___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] ,
// lsl is above
// lsr is above
// asr is above
"adc" : [ { base : "0100000101___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // d + s + carry
"sbc" : [ { base : "0100000110___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // d - s - !carry
"ror" : [ { base : "0100000111___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // rotate right
"tst" : [ { base : "0100001000___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // test
"neg" : [ { base : "0100001001___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // - s
// cmp is above
"cmn" : [ { base : "0100001011___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // test d+s
"orr" : [ { base : "0100001100___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // |
"mul" : [ { base : "0100001101___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // s*d
"bic" : [ { base : "0100001110___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // d & ~s
"mvn" : [ { base : "0100001111___---" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } ] , // ~s
// 5.5 Format 5: Hi register operations/branch exchange
// 5.6 Format 6: PC-relative load
// done (below)
// 5.7 Format 7: load/store with register offset
// done (below)
// 5.8 Format 8: load/store sign-extended byte/halfword
// 5.9 Format 9: load/store with immediate offset
// done (below)
// 5.10 Format 10: load/store halfword
// 5.11 Format 11: SP-relative load/store
// 5.12 Format 12: load address
// done (below)
// 5.13 Format 13: add offset to Stack Pointer
// 5.14 Format 14: push/pop registers
// done (below)
// 5.16 Format 16: conditional branch
"beq" : [ { base : "11010000________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bne" : [ { base : "11010001________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bcs" : [ { base : "11010010________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bcc" : [ { base : "11010011________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bmi" : [ { base : "11010100________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bpl" : [ { base : "11010101________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bvs" : [ { base : "11010110________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bvc" : [ { base : "11010111________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bhi" : [ { base : "11011000________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bls" : [ { base : "11011001________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bge" : [ { base : "11011010________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"blt" : [ { base : "11011011________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"bgt" : [ { base : "11011100________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
"ble" : [ { base : "11011101________" , regex : /^(.*)$/ , args : [ sint ( 0 , 8 , 1 ) ] } ] , // 5.16 Format 16: conditional branch
// 5.17 Format 17: software interrupt
// 5.18 Format 18: unconditional branch
"b" : [ { base : "11100___________" , regex : /^(.*)$/ , args : [ sint ( 0 , 11 , 1 ) ] } ] ,
// 5.19 Format 19: long branch with link
"bl" : [ { base : "11110___________11111___________" , regex : /^(.*)$/ , args : [ bl _addr ( ) ] } ] ,
"bx" : [ { base : "010001110----000" , regex : /(lr|r[0-9]+)$/ , args : [ reg4 ( 3 ) ] } ] ,
// ....
"adr" : [ { base : "10100---________" , regex : /^(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // ADR pseudo-instruction to save address (actually ADD PC)
{ base : "10100---________" , regex : /^(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*\+[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } ] ,
"push" : [ { base : "1011010-________" , regex : /^{(.*)}$/ , args : [ rlist _lr ] } ] , // 5.14 Format 14: push/pop registers
"pop" : [ { base : "1011110-________" , regex : /^{(.*)}$/ , args : [ rlist _lr ] } ] , // 5.14 Format 14: push/pop registers
"add" : [ { base : "00110---________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } , // move/compare/subtract immediate
{ base : "10100---________" , regex : /^(r[0-7]),pc,(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } ,
{ base : "10101---________" , regex : /^(r[0-7]),sp,(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } ,
{ base : "101100000_______" , regex : /^sp,(#[0-9]+)$/ , args : [ uint ( 0 , 7 , 2 ) ] } ,
{ base : "00011-0___---___" , regex : /^(r[0-7]),(r[0-7]),([^,]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg _or _immediate ( 6 , 10 ) ] } ] , // Format 2: add/subtract
"adds" : [ { base : "00011-0___---___" , regex : /^(r[0-7]),(r[0-7]),([^,]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg _or _immediate ( 6 , 10 ) ] } ] , //?
"adc.w" : [ { base : "111010110100----________--------" , regex : /^(r[0-7]),(r[0-7]),(r[0-7])$/ , args : [ reg ( 16 ) , reg ( 8 ) , reg ( 0 ) ] } ] , // made this up. probably wrong
"add.w" : [ { base : "11110001--------________--------" , regex : /^(r[0-7]),(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 16 ) , reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } ] , // made this up. probably wrong
"sub" : [ { base : "00111---________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } , // move/compare/subtract immediate
/*{ base:"10100---________", regex : /^([^,]+),pc,(#[0-9]+)$/,args:[reg(8),uint(0,8,2)] },*/
{ base : "101100001_______" , regex : /^sp,(#[0-9]+)$/ , args : [ uint ( 0 , 7 , 2 ) ] } ,
{ base : "00011-1___---___" , regex : /^([^,]+),([^,]+),([^,]+)$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg _or _immediate ( 6 , 10 ) ] } ] ,
"str" : [ { base : "0101000---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "10010---________" , regex : /(r[0-7]),\[sp,(#[0-9]+)\]$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // 5.11 SP-relative store
{ base : "0110000000___---" , regex : /(r[0-7]),\[(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } , // 5.9 Format 9: load/store with no offset
{ base : "0110000---___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 2 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"strb" : [ { base : "0101010---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "01110-----___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 0 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"strh" : [ { base : "0101001---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "10000-----___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 1 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"ldr" : [ { base : "01001---________" , regex : /(r[0-7]),\[pc,(#[0-9]+)\]$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // 5.6 Format 6: PC-relative load
{ base : "10011---________" , regex : /(r[0-7]),\[sp,(#[0-9]+)\]$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // 5.11 SP-relative load
{ base : "01001---________" , regex : /(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // 5.6 Format 6: PC-relative load (using label)
{ base : "01001---________" , regex : /(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*\+[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 2 ) ] } , // 5.6 Format 6: PC-relative load (using label and maths - huge hack)
{ base : "0101100---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "0110100000___---" , regex : /(r[0-7]),\[(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } , // 5.9 Format 9: load/store with no offset
{ base : "0110100---___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 2 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"ldrb" : [ { base : "0101110---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "01111-----___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 0 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"ldrsb" : [ { base : "0101011---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } ] , // 5.7 Format 7: load/store with register offset
"ldrh" : [ { base : "0101101---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } , // 5.7 Format 7: load/store with register offset
{ base : "10001-----___---" , regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , uint ( 6 , 5 , 1 ) ] } ] , // 5.9 Format 9: load/store with immediate offset
"ldrsh" : [ { base : "0101111---___---" , regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/ , args : [ reg ( 0 ) , reg ( 3 ) , reg ( 6 ) ] } ] , // 5.7 Format 7: load/store with register offset
"mov" : [ { base : "00100---________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } , // move/compare/subtract immediate
{ base : "0001110000---___" , regex : /(r[0-7]),(r[0-7])$/ , args : [ reg ( 0 ) , reg ( 3 ) ] } , // actually 'add Rd,Rs,#0'
{ base : "0100011010---101" , regex : /sp,(r[0-7])$/ , args : [ reg ( 3 ) ] } ] , // made up again
"movs" : [ { base : "00100---________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg ( 8 ) , uint ( 0 , 8 , 0 ) ] } ] , // is this even in thumb?
"movw" : [ { base : "11110-100100----0___----________" , regex : /(r[0-7]),(#[0-9]+)$/ , args : [ reg4 ( 8 ) , thumb2 _immediate _t3 ] } ] ,
".word" : [ { base : "--------------------------------" , regex : /0x([0-9A-Fa-f]+)$/ , args : [ function ( v ) { v = parseInt ( v , 16 ) ; return ( v >> 16 ) | ( v << 16 ) ; } ] } ,
{ base : "--------------------------------" , regex : /([0-9]+)$/ , args : [ function ( v ) { v = parseInt ( v ) ; return ( v >> 16 ) | ( v << 16 ) ; } ] } ] ,
"nop" : [ { base : "0100011011000000" , regex : "" , args : [ ] } ] , // MOV R8,R8 (Format 5)
"cpsie" : [ { base : "1011011001100010" , regex : /i/ , args : [ ] } ] , // made up again
"cpsid" : [ { base : "1011011001110010" , regex : /i/ , args : [ ] } ] , // made up again
"wfe" : [ { base : "1011111100100000" , regex : /i/ , args : [ ] } ] ,
"wfi" : [ { base : "1011111100110000" , regex : /i/ , args : [ ] } ] ,
// for this, uint needs to work without a hash
// "swi" :[{ base:"11011111--------", regex : /([0-9]+)$/, args:[uint(0,8,0)] }], // Format 17: software interrupt
} ;
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function getOpCode ( binary ) {
var base = "" ;
for ( var b in binary )
if ( "-_" . indexOf ( binary [ b ] ) >= 0 )
base += "0" ;
else
base += binary [ b ] ;
var opCode = parseInt ( base , 2 ) ;
if ( opCode < 0 ) opCode = opCode + 2147483648.0 ;
return opCode ;
}
function assemble _internal ( asmLines , wordCallback , labels ) {
var addr = 0 ;
var newLabels = { } ;
asmLines . forEach ( function ( line ) {
// setup labels
if ( labels !== undefined )
labels [ "PC" ] = addr + 4 ;
// handle line
line = line . trim ( ) ;
if ( line == "" ) return ;
if ( line . substr ( - 1 ) == ":" ) {
// it's a label
var labelName = line . substr ( 0 , line . length - 1 ) ;
if ( newLabels [ labelName ] !== undefined )
throw "Label '" + labelName + "' was already defined" ;
newLabels [ labelName ] = addr ;
return ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
// parse instruction
var firstArgEnd = line . indexOf ( "\t" ) ;
if ( firstArgEnd < 0 ) firstArgEnd = line . indexOf ( " " ) ;
if ( firstArgEnd < 0 ) firstArgEnd = line . length ;
var opName = line . substr ( 0 , firstArgEnd ) ;
var args = line . substr ( firstArgEnd ) . replace ( /[ \t]/g , "" ) . trim ( ) ;
if ( ! ( opName in ops ) ) throw "Unknown Op '" + opName + "' in '" + line + "'" ;
// search ops
var found = false ;
for ( var n in ops [ opName ] ) {
var op = ops [ opName ] [ n ] ;
var m ;
if ( m = args . match ( op . regex ) ) {
found = true ;
// work out the base opcode
var opCode = getOpCode ( op . base ) ;
if ( labels !== undefined ) {
/ * I f w e ' r e p r o p e r l y g e n e r a t i n g c o d e , p a r s e e a c h a r g u m e n t .
Otherwise we ' re just working out the size in bytes of each line
and we can skip this * /
for ( var i in op . args ) {
//console.log(i,m[(i|0)+1]);
var argFunction = op . args [ i ] ;
var bits = argFunction ( m [ ( i | 0 ) + 1 ] , labels ) ;
//console.log(" ",bits)
opCode |= bits ;
}
}
if ( op . base . length > 16 ) {
wordCallback ( ( opCode >>> 16 ) ) ;
wordCallback ( opCode & 0xFFFF ) ;
addr += 4 ;
} else {
wordCallback ( opCode ) ;
addr += 2 ;
}
break ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
}
// now parse args
if ( ! found )
throw "Unknown arg style '" + args + "' in '" + line + "'" ;
} ) ;
return newLabels ;
}
function assemble ( asmLines , wordCallback ) {
// remove line comments
asmLines = asmLines . map ( function ( l ) {
var i ;
i = l . indexOf ( ";" ) ;
if ( i >= 0 ) l = l . substr ( 0 , i ) ;
i = l . indexOf ( "//" ) ;
if ( i >= 0 ) l = l . substr ( 0 , i ) ;
return l ;
} ) ;
// process assembly to grab labels
var labels = assemble _internal ( asmLines , function ( ) { } , undefined ) ;
console . log ( "Assembler Labels:" , labels ) ;
// process again to actually get an output
assemble _internal ( asmLines , wordCallback , labels ) ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function init ( ) {
// When code is sent to Espruino, search it for bits of assembler and then assemble them
Espruino . addProcessor ( "transformForEspruino" , function ( code , callback ) {
findASMBlocks ( code , "" , callback ) ;
} ) ;
// When a module is sent to Espruino...
Espruino . addProcessor ( "transformModuleForEspruino" , function ( module , callback ) {
findASMBlocks ( module . code , " in " + module . name , function ( code ) {
module . code = code ;
callback ( module ) ;
} ) ;
} ) ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function assembleBlock ( asmLines , description ) {
var machineCode = [ ] ;
try {
assemble ( asmLines , function ( word ) { machineCode . push ( "0x" + word . toString ( 16 ) ) ; } ) ;
} catch ( err ) {
console . log ( "Assembler failed: " + err + description ) ;
Espruino . Core . Notifications . error ( "Assembler failed: " + err + description ) ;
return undefined ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
return machineCode ;
}
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
/* Finds instances of 'E.asm' and replaces them */
function findASMBlocks ( code , description , callback ) {
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
function match ( str , type ) {
if ( str !== undefined && tok . str != str ) {
Espruino . Core . Notifications . error ( "Expecting '" + str + "' but got '" + tok . str + description + "'. Should have E.asm('arg spec', 'asmline1', ..., 'asmline2'" ) ;
2020-04-27 15:12:13 +00:00
return false ;
2020-04-29 08:20:18 +00:00
}
if ( type !== undefined && tok . type != type ) {
Espruino . Core . Notifications . error ( "Expecting a " + type + " but got " + tok . type + description + ". Should have E.asm('arg spec', 'asmline1', ..., 'asmline2'" ) ;
2020-04-27 15:12:13 +00:00
return false ;
2020-04-29 08:20:18 +00:00
}
tok = lex . next ( ) ;
return true ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
var foundAsm = true ;
var assembledCode = "" ;
var asmBlockCount = 1 ;
while ( foundAsm ) {
foundAsm = false ;
var lex = Espruino . Core . Utils . getLexer ( code ) ;
var tok = lex . next ( ) ;
var state = 0 ;
var startIndex = - 1 ;
while ( tok !== undefined ) {
if ( state == 0 && tok . str == "E" ) { state = 1 ; startIndex = tok . startIdx ; tok = lex . next ( ) ;
} else if ( state == 1 && tok . str == "." ) { state = 2 ; tok = lex . next ( ) ;
} else if ( state == 2 && ( tok . str == "asm" ) ) { state = 3 ; tok = lex . next ( ) ;
} else if ( state == 3 && ( tok . str == "(" ) ) {
foundAsm = true ;
state = 0 ;
tok = lex . next ( ) ; // skip (
var argSpec = tok . value ;
var asmLines = [ ] ;
if ( ! match ( undefined , "STRING" ) ) return ;
if ( ! match ( "," , undefined ) ) return ;
while ( tok && tok . str != ")" ) {
var lines = tok . value . split ( "\n" ) ;
lines . forEach ( function ( l ) {
asmLines . push ( l ) ;
} ) ;
if ( ! match ( undefined , "STRING" ) ) return ;
if ( tok . str != ")" )
if ( ! match ( "," , undefined ) ) return ;
}
if ( ! match ( ")" , undefined ) ) return ;
var endIndex = tok . endIdx ;
var machineCode = assembleBlock ( asmLines , description ) ;
//console.log(machineCode);
if ( machineCode === undefined ) return ; // There was an error - just leave and don't try to flash
var raw = "" ;
machineCode . forEach ( function ( short ) {
var v = parseInt ( short , 16 ) ;
raw += String . fromCharCode ( v & 255 , v >> 8 ) ;
} ) ;
var base64 = base64 _encode ( raw ) ;
code = code . substr ( 0 , startIndex ) +
'E.nativeCall(1, ' + JSON . stringify ( argSpec ) + ', atob(' + JSON . stringify ( base64 ) + '))' +
code . substr ( endIndex ) ;
asmBlockCount ++ ;
// Break out
tok = undefined ;
2020-04-27 15:12:13 +00:00
} else {
2020-04-29 08:20:18 +00:00
state = 0 ;
tok = lex . next ( ) ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
}
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
if ( assembledCode != "" ) {
code = "var ASM_BASE=process.memory().stackEndAddress;\n" +
assembledCode +
code ;
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
callback ( code ) ;
} ;
2020-04-27 15:12:13 +00:00
2020-04-29 08:20:18 +00:00
Espruino . Plugins . Assembler = {
init : init ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
2020-04-27 15:12:13 +00:00
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
2020-04-29 08:20:18 +00:00
2020-04-27 15:12:13 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2020-04-29 08:20:18 +00:00
Try and get any URLS that are from GitHub
2020-04-27 15:12:13 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
2020-04-29 08:20:18 +00:00
2020-04-27 15:12:13 +00:00
function init ( ) {
2020-04-29 08:20:18 +00:00
Espruino . addProcessor ( "getURL" , getGitHub ) ;
2020-04-27 16:00:54 +00:00
}
2020-04-29 08:20:18 +00:00
function getGitHub ( data , callback ) {
var match = data . url . match ( /^https?:\/\/github.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.*)$/ ) ;
if ( match ) {
var git = {
owner : match [ 1 ] ,
repo : match [ 2 ] ,
branch : match [ 3 ] ,
path : match [ 4 ]
} ;
var url = "https://raw.githubusercontent.com/" + git . owner + "/" + git . repo + "/" + git . branch + "/" + git . path ;
console . log ( "Found GitHub" , JSON . stringify ( git ) ) ;
callback ( { url : url } ) ;
} else
callback ( data ) ; // no match - continue as normal
2020-04-27 15:12:13 +00:00
}
2020-04-29 08:20:18 +00:00
Espruino . Plugins . GetGitHub = {
2020-04-27 15:12:13 +00:00
init : init ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Pretokenise code before it uploads
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
2020-04-27 16:32:12 +00:00
if ( typeof acorn == "undefined" ) {
console . log ( "pretokenise: needs acorn, disabling." ) ;
}
2020-04-27 15:12:13 +00:00
function init ( ) {
Espruino . Core . Config . add ( "PRETOKENISE" , {
section : "Minification" ,
name : "Pretokenise code before upload (BETA)" ,
description : "All whitespace and comments are removed and all reserved words are converted to tokens before upload. This means a faster upload, less memory used, and increased performance (+10%) at the expense of code readability." ,
type : "boolean" ,
defaultValue : false
} ) ;
// When code is sent to Espruino, search it for modules and add extra code required to load them
Espruino . addProcessor ( "transformForEspruino" , function ( code , callback ) {
if ( ! Espruino . Config . PRETOKENISE ) return callback ( code ) ;
pretokenise ( code , callback ) ;
} ) ;
// When code is sent to Espruino, search it for modules and add extra code required to load them
Espruino . addProcessor ( "transformModuleForEspruino" , function ( module , callback ) {
if ( ! Espruino . Config . PRETOKENISE ) return callback ( module ) ;
pretokenise ( module . code , function ( code ) {
module . code = code ;
callback ( module ) ;
} ) ;
} ) ;
}
var LEX _OPERATOR _START = 138 ;
var TOKENS = [ // plundered from jslex.c
/* LEX_EQUAL : */ "==" ,
/* LEX_TYPEEQUAL : */ "===" ,
/* LEX_NEQUAL : */ "!=" ,
/* LEX_NTYPEEQUAL : */ "!==" ,
/* LEX_LEQUAL : */ "<=" ,
/* LEX_LSHIFT : */ "<<" ,
/* LEX_LSHIFTEQUAL : */ "<<=" ,
/* LEX_GEQUAL : */ ">=" ,
/* LEX_RSHIFT : */ ">>" ,
/* LEX_RSHIFTUNSIGNED */ ">>>" ,
/* LEX_RSHIFTEQUAL : */ ">>=" ,
/* LEX_RSHIFTUNSIGNEDEQUAL */ ">>>=" ,
/* LEX_PLUSEQUAL : */ "+=" ,
/* LEX_MINUSEQUAL : */ "-=" ,
/* LEX_PLUSPLUS : */ "++" ,
/* LEX_MINUSMINUS */ "--" ,
/* LEX_MULEQUAL : */ "*=" ,
/* LEX_DIVEQUAL : */ "/=" ,
/* LEX_MODEQUAL : */ "%=" ,
/* LEX_ANDEQUAL : */ "&=" ,
/* LEX_ANDAND : */ "&&" ,
/* LEX_OREQUAL : */ "|=" ,
/* LEX_OROR : */ "||" ,
/* LEX_XOREQUAL : */ "^=" ,
/* LEX_ARROW_FUNCTION */ "=>" ,
// reserved words
/*LEX_R_IF : */ "if" ,
/*LEX_R_ELSE : */ "else" ,
/*LEX_R_DO : */ "do" ,
/*LEX_R_WHILE : */ "while" ,
/*LEX_R_FOR : */ "for" ,
/*LEX_R_BREAK : */ "break" ,
/*LEX_R_CONTINUE */ "continue" ,
/*LEX_R_FUNCTION */ "function" ,
/*LEX_R_RETURN */ "return" ,
/*LEX_R_VAR : */ "var" ,
/*LEX_R_LET : */ "let" ,
/*LEX_R_CONST : */ "const" ,
/*LEX_R_THIS : */ "this" ,
/*LEX_R_THROW : */ "throw" ,
/*LEX_R_TRY : */ "try" ,
/*LEX_R_CATCH : */ "catch" ,
/*LEX_R_FINALLY : */ "finally" ,
/*LEX_R_TRUE : */ "true" ,
/*LEX_R_FALSE : */ "false" ,
/*LEX_R_NULL : */ "null" ,
/*LEX_R_UNDEFINED */ "undefined" ,
/*LEX_R_NEW : */ "new" ,
/*LEX_R_IN : */ "in" ,
/*LEX_R_INSTANCEOF */ "instanceof" ,
/*LEX_R_SWITCH */ "switch" ,
/*LEX_R_CASE */ "case" ,
/*LEX_R_DEFAULT */ "default" ,
/*LEX_R_DELETE */ "delete" ,
/*LEX_R_TYPEOF : */ "typeof" ,
/*LEX_R_VOID : */ "void" ,
/*LEX_R_DEBUGGER : */ "debugger" ,
/*LEX_R_CLASS : */ "class" ,
/*LEX_R_EXTENDS : */ "extends" ,
/*LEX_R_SUPER : */ "super" ,
/*LEX_R_STATIC : */ "static" ,
/*LEX_R_OF : */ "of"
] ;
function pretokenise ( code , callback ) {
2020-04-27 16:32:12 +00:00
var lex = ( function ( ) {
var t = acorn . tokenizer ( code ) ;
return { next : function ( ) {
var tk = t . getToken ( ) ;
if ( tk . type . label == "eof" ) return undefined ;
var tp = "?" ;
if ( tk . type . label == "template" || tk . type . label == "string" ) tp = "STRING" ;
if ( tk . type . label == "num" ) tp = "NUMBER" ;
if ( tk . type . keyword ) tp = "ID" ;
return {
startIdx : tk . start ,
endIdx : tk . end ,
str : code . substr ( tk . start , tk . end ) ,
type : tp
} ;
} } ;
} ) ( ) ;
2020-04-27 15:12:13 +00:00
var brackets = 0 ;
var resultCode = "" ;
var lastIdx = 0 ;
var lastTok = { str : "" } ;
var tok = lex . next ( ) ;
while ( tok !== undefined ) {
var previousString = code . substring ( lastIdx , tok . startIdx ) ;
var tokenString = code . substring ( tok . startIdx , tok . endIdx ) ;
var tokenId = LEX _OPERATOR _START + TOKENS . indexOf ( tokenString ) ;
if ( tokenId < LEX _OPERATOR _START ) tokenId = undefined ;
//console.log("prev "+JSON.stringify(previousString)+" next "+tokenString);
if ( tok . str == "(" || tok . str == "{" || tok . str == "[" ) brackets ++ ;
// TODO: check for eg. two IDs/similar which can't be merged without a space
// preserve newlines at root scope to avoid us filling up the command buffer all at once
if ( brackets == 0 && previousString . indexOf ( "\n" ) >= 0 )
resultCode += "\n" ;
2020-04-27 16:00:54 +00:00
if ( tok . str == ")" || tok . str == "}" || tok . str == "]" ) brackets -- ;
2020-04-27 15:12:13 +00:00
// if we have a token for something, use that - else use the string
if ( tokenId ) {
//console.log(JSON.stringify(tok.str)+" => "+tokenId);
resultCode += String . fromCharCode ( tokenId ) ;
2020-04-27 16:00:54 +00:00
tok . type = "TOKENISED" ;
2020-04-27 15:12:13 +00:00
} else {
if ( ( tok . type == "ID" || tok . type == "NUMBER" ) &&
( lastTok . type == "ID" || lastTok . type == "NUMBER" ) )
resultCode += " " ;
resultCode += tokenString ;
}
// next
lastIdx = tok . endIdx ;
lastTok = tok ;
tok = lex . next ( ) ;
}
callback ( resultCode ) ;
}
Espruino . Plugins . Pretokenise = {
init : init ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk ) ,
Victor Nakoryakov ( victor @ amperka . ru )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Wrap whole code in ` onInit ` function before send and save ( ) it
after upload . Wrapping is necessary to avoid execution start
before save ( ) is executed
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
function init ( ) {
Espruino . Core . Config . add ( "SAVE_ON_SEND" , {
section : "Communications" ,
name : "Save on Send" ,
descriptionHTML : 'How should code be uploaded? See <a href="http://www.espruino.com/Saving" target="_blank">espruino.com/Saving</a> for more information.<br>' +
"<b>NOTE:</b> Avoid 'Direct to flash, even after <code>reset()</code>' for normal development - it can make it hard to recover if your code crashes the device." ,
type : {
0 : "To RAM (default) - execute code while uploading. Use 'save()' to save a RAM image to Flash" ,
1 : "Direct to Flash (execute code at boot)" ,
2 : "Direct to Flash (execute code at boot, even after 'reset()') - USE WITH CARE" ,
3 : "To Storage File (see 'File in Storage to send to')" ,
} ,
defaultValue : 0
} ) ;
Espruino . Core . Config . add ( "SAVE_STORAGE_FILE" , {
section : "Communications" ,
name : "Send to File in Storage" ,
descriptionHTML : "If <code>Save on Send</code> is set to <code>To Storage File</code>, this is the name of the file to write to." ,
type : "string" ,
defaultValue : "myapp"
} ) ;
Espruino . Core . Config . add ( "LOAD_STORAGE_FILE" , {
section : "Communications" ,
name : "Load after saving" ,
descriptionHTML : "This applies only if saving to Flash (not RAM)" ,
type : {
0 : "Don't load" ,
1 : "Load default application" ,
2 : "Load the Storage File just written to"
} ,
defaultValue : 2
} ) ;
Espruino . addProcessor ( "transformForEspruino" , function ( code , callback ) {
wrap ( code , callback ) ;
} ) ;
}
function wrap ( code , callback ) {
var isFlashPersistent = Espruino . Config . SAVE _ON _SEND == 2 ;
var isStorageUpload = Espruino . Config . SAVE _ON _SEND == 3 ;
var isFlashUpload = Espruino . Config . SAVE _ON _SEND == 1 || isFlashPersistent || isStorageUpload ;
if ( ! isFlashUpload ) return callback ( code ) ;
// Check environment vars
var hasStorage = false ;
var ENV = Espruino . Core . Env . getData ( ) ;
if ( ENV &&
ENV . VERSION _MAJOR &&
ENV . VERSION _MINOR !== undefined ) {
if ( ENV . VERSION _MAJOR > 1 ||
ENV . VERSION _MINOR >= 96 ) {
hasStorage = true ;
}
}
//
console . log ( "Uploading " + code . length + " bytes to flash" ) ;
if ( ! hasStorage ) { // old style
if ( isStorageUpload ) {
Espruino . Core . Notifications . error ( "You have pre-1v96 firmware - unable to upload to Storage" ) ;
code = "" ;
} else {
Espruino . Core . Notifications . error ( "You have pre-1v96 firmware. Upload size is limited by available RAM" ) ;
code = "E.setBootCode(" + JSON . stringify ( code ) + ( isFlashPersistent ? ",true" : "" ) + ");load()\n" ;
}
} else { // new style
var filename ;
if ( isStorageUpload )
filename = Espruino . Config . SAVE _STORAGE _FILE ;
else
filename = isFlashPersistent ? ".bootrst" : ".bootcde" ;
if ( ! filename || filename . length > 28 ) {
Espruino . Core . Notifications . error ( "Invalid Storage file name " + JSON . stringify ( filename ) ) ;
code = "" ;
} else {
var CHUNKSIZE = 1024 ;
var newCode = [ ] ;
var len = code . length ;
newCode . push ( 'require("Storage").write("' + filename + '",' + JSON . stringify ( code . substr ( 0 , CHUNKSIZE ) ) + ',0,' + len + ');' ) ;
for ( var i = CHUNKSIZE ; i < len ; i += CHUNKSIZE )
newCode . push ( 'require("Storage").write("' + filename + '",' + JSON . stringify ( code . substr ( i , CHUNKSIZE ) ) + ',' + i + ');' ) ;
code = newCode . join ( "\n" ) ;
if ( Espruino . Config . LOAD _STORAGE _FILE == 2 && isStorageUpload )
code += "\nload(" + JSON . stringify ( filename ) + ")\n" ;
else if ( Espruino . Config . LOAD _STORAGE _FILE != 0 )
code += "\nload()\n" ;
}
}
callback ( code ) ;
}
Espruino . Plugins . SaveOnSend = {
init : init ,
} ;
} ( ) ) ;
/ * *
Copyright 2014 Gordon Williams ( gw @ pur3 . co . uk )
This Source Code is subject to the terms of the Mozilla Public
License , v2 . 0. If a copy of the MPL was not distributed with this
file , You can obtain one at http : //mozilla.org/MPL/2.0/.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Ability to set the current time in Espruino
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* * /
"use strict" ;
( function ( ) {
function init ( ) {
Espruino . Core . Config . add ( "SET_TIME_ON_WRITE" , {
section : "Communications" ,
name : "Set Current Time" ,
description : "When sending code, set Espruino's clock to the current time" ,
type : "boolean" ,
defaultValue : true ,
onChange : function ( newValue ) { }
} ) ;
// When code is sent to Espruino, append code to set the current time
Espruino . addProcessor ( "transformForEspruino" , function ( code , callback ) {
if ( Espruino . Config . SET _TIME _ON _WRITE ) {
var time = new Date ( ) ;
code = "setTime(" + ( time . getTime ( ) / 1000 ) + ");E.setTimeZone(" + ( - time . getTimezoneOffset ( ) / 60 ) + ")\n" + code ;
}
callback ( code ) ;
} ) ;
}
Espruino . Plugins . SetTime = {
init : init ,
} ;
} ( ) ) ;
Espruino . transform = function ( code , options ) {
return new Promise ( function ( resolve , reject ) {
Object . keys ( options ) . forEach ( function ( key ) {
if ( key == key . toUpperCase ( ) )
Espruino . Config [ key ] = options [ key ] ;
} ) ;
if ( options . builtinModules ) {
var d = Espruino . Core . Env . getData ( ) ;
d . MODULES = options . builtinModules ;
}
Espruino . callProcessor ( "transformForEspruino" , code , resolve ) ;
} ) ;
} ;